Semantic GraphQL provides an API to convert any RDF, RDFS and OWL-based ontologies into GraphQL objects.
The library does not deal with data, only with terminology. Therefore resolving data/resources is up to you.
Table of contents:
Runs on Node v6 or higher.
npm install semantic-graphql --save
Semantic-graphql makes no assumption about the shape of your data and only passes it around. You have to provide six functions to resolve it in different ways. See the resolvers section.
const resolvers = { /* Choose how to resolve data */ };
Once your resolvers are written you can create a SemanticGraph. In the graph all the triples defining RDF, RDFS and OWL are included by default.
const SemanticGraph = require('semantic-graphql');
const _ = new SemanticGraph(resolvers, config);
Now you can add your own triples. Only statements defining your terminology (classes + properties) will be used, so you shouldn't add your individuals as it will only consume memory.
_.addTriple({
subject: 'http://foo.com#MyClass',
predicate: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
object: 'http://www.w3.org/2002/07/owl#Class',
});
// From a string or a file. Multiple formats are available.
_.parse(string);
_.parseFile('/path/to/ontology.ttl');
When Semantic-graphql translates a rdf:Property to a GraphQLFieldConfig, the resulting type will be wrapped in a GraphQLList unless the property is a owl:FunctionalProperty. Therefore you may have to do some adjustments in order to prevent that. Almost anything can be overriden, see the override section.
// We do not want rdfs:label to resolve arrays
_['http://www.w3.org/2000/01/rdf-schema#label'].isGraphqlList = false;
Now you can start building your GraphQL schema however you want.
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
myField: {
// GraphQL objects are created on demand using the API
type: _.getObjectType('http://foo.com#MyClass'),
resolve: /* ... */,
},
resource: {
// The rdfs:Resource interface allows you to query any data
type: _.getInterfaceType('http://www.w3.org/2000/01/rdf-schema#Resource'),
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: /* ... */,
},
},
}),
});
Have a look at the examples folder to see a complete setup.
class SemanticGraph {
constructor(resolvers: Resolvers, config: ?SemanticGraphConfig),
nTriples: number,
# The semantic data is stored directly on the graph
[subject: Iri]: { [predicate: iri]: Array<object: string> },
# Public methods:
addTriple: AddTripleFn,
parse: ParseFn,
parseFile: ParseFileFn,
getObjectType: GetObjectTypeFn,
getInterfaceType: GetInterfaceTypeFn,
getEdgeType: GetEdgeTypeFn,
getConnectionType: GetConnectionTypeFn,
addFieldOnObjectType: AddFieldOnObjectTypeFn,
extendFieldOnObjectType: ExtendFieldOnObjectTypeFn,
# When the relay option is on:
nodeField: GraphQLFieldConfig,
nodeInterface: GraphQLInterfaceType,
}
type SemanticGraphConfig = {
prefixes?: PrefixConfig,
# Activates the Relay features
relay?: boolean,
# Your reprefered locale when inferring names and descriptions from rdfs:label and rdfs:comment
locale?: string = 'en',
# Prevents the id field to be automatically added to every GraphlQLFieldConfigMap
preventIdField?: boolean,
}
To prevent GraphQL names collisions, you can edit the name directly (see the override section) or specifify prefixes for ontology namespaces. The names of the generated GraphQL objects will be prefixed. RDF, RDFS and OWL ontologies are by default prefixed with "rdf", "rdfs" and "owl". So a fragment on rdfs:Resource should be "on RdfsResource".
type PrefixConfig = {
[prefix: string]: Iri,
}
# Must represent a valid IRI
type Iri = string
type AddTripleFn(triple: Triple) => undefined
type Triple = {
subject: Iri,
predicate: Iri,
object: Iri | string,
}
Appends a triple to the graph. No-op if the subject or predicate IRI is invalid, or if the triple already exists.
Deprecated
Deprecated
type GetObjectTypeFn = (classIri: Iri) => GraphQLObjectType
Returns the GraphQLObjectType corresponding to a given individual of rdfs:Class or its sub-classes (like owl:Class). Throws if the IRI is not found in the graph.
type GetInterfaceTypeFn = (classIri: Iri) => GraphQLInterfaceType
type GetEdgeTypeFn = (classIri: Iri) => ?RelayEdgeType
Returns a value only when the relay: true
option is on.
type GetConnectionTypeFn = (classIri: Iri) => ?RelayConnectionType
Returns a value only when the relay: true
option is on.
type AddFieldOnObjectTypeFn = (
classIri: Iri,
fieldName: string,
graphqlFieldConfig: GraphQLFieldConfig
) => Iri
Adds a custom field on the GraphQLObjectType and GraphQLInterfaceType representing a class. Throws if "classIri" is not found in the graph. Returns a random IRI referencing the new virtual rdf:Property.
type AddFieldOnObjectTypeFn = (
classIri: Iri,
propertyIri: Iri,
graphqlFieldConfig: PseudoGraphQLFieldConfig
) => undefined
Similar to overriding a field using _['http://foo.com/someProperty'].graphqlFieldConfigExtension = /* ... */
but only for a particular class, not for all of the classes on the property's domain.
A PseudoGraphQLFieldConfig
is just a GraphQLFieldConfig
where every key is optional. The other keys will be infered.
Throws if "classIri" is not found in the graph.
Resolvers are functions needed to resolve data. You have to code them.
type Resolvers = {
resolveSourceId: ResolveSourceIdFn,
resolveSourceTypes: ResolveSourceTypesFn,
resolveSourcePropertyValue: ResolveSourcePropertyValueFn,
resolveResource: ResolveResourceFn,
resolveResources: ResolveResourcesFn,
resolveResourcesByPredicate: ResolveResourcesByPredicateFn,
}
type ResolverOutput<x> = x | Array<x> | Promise<x> | Promise<Array<x>>
Given a source, resolve its id.
type ResolveSourceIdFn = (
source?: any,
context?: any,
info?: GraphQLResolveInfo
) => ?ID | ?Promise<ID>
Must be sync if you use Relay.
See globalIdField
's source code
Given a source, resolve its rdf:type. Can be a single IRI since its super-classes will be infered.
type ResolveSourceTypesFn = (
source?: any,
info?: GraphQLResolveInfo
) => ResolverOutput<class: Iri>
Given a source and the IRI of a predicate, resolve the objects for that source and predicate.
type ResolveSourcePropertyValueFn = (
source?: any,
propertyIri?: Iri,
context?: any,
info?: GraphQLResolveInfo
) => ?ResolverOutput<any>
Given the IRI of a resource, resolve a "source" for other resolvers to use. You can either return the resource IRI to pass around as source, or fetch all the predicates/objects for that subject and return an object.
type ResolveResourceFn = (
resourceIri?: Iri,
context?: any,
info?: GraphQLResolveInfo
) => ?ResolverOutput<any>
Same as resolveResource
but with an array of IRIs.
type ResolveResourcesFn = (
resourceIris?: Array<Iri>,
context?: any,
info?: GraphQLResolveInfo
) => ?ResolverOutput<any>
Given a array of possible rdf:type, a predicate and an object, resolve the subjects matching that pattern.
type ResolveResourcesByPredicateFn = (
typeIris?: Array<Iri>,
predicateIri?: Iri,
value?: any,
context?: any,
info?: GraphQLResolveInfo
) => ?ResolverOutput<any>
To override anything there is no method: you directly set the library's internals.
_['http://the.resource.to/be#altered'].key = value;
If "key" is already present, the library will use the underlying value and won't try to create it.
Resource type | key | value type |
---|---|---|
any | graphqlName | String |
any | graphqlDescription | String |
class | graphqlFieldConfigMap | GraphQLFieldConfigMap |
class | graphqlObjectType | GraphQLObjectType |
class | graphqlInterfaceType | GraphQLInterfaceType |
class | relayConnectionDefinitions | { edgeType, connectionType } |
property | isGraphqlList | Boolean |
property | isRelayConnection | Boolean |
property | graphqlFieldConfig | GraphQLFieldConfig |
property | graphqlFieldConfigExtension | partial GraphQLFieldConfig, to modify only parts of it |
property | shouldAlwaysUseInverseOf | Boolean |
property | shouldNeverUseInverseOf | Boolean |
Note that the following overrides must be performed before invoking getObjectType
or getInterfaceType
or getEdgeType
or getConnectionType
since those methods create the GraphQL objects you want to override.
Examples:
_['http://foo.com#worksForCompany'].graphqlName = 'company';
// Now the field name for foo:worksForCompany will be 'company'
// Partial modifications to fields are achieved using
_['http://foo.com#someOtherProperty'].graphqlFieldConfigExtension = {
args: /* Look Ma', arguments! */
resolve: customResolveFn,
};
// Completly overriding a GraphQLObject can be done with
_['http://foo.com/MyClass'].graphqlObjectType = new GraphQLObjectType({ /* ... */});
By using the relay: true
option:
_.nodeField
and _.nodeInterface
object are available_.getConnectionType
and _.getEdgeType
methods are availableSee the Relay example.
The following features will not be implemented due to their incomptability with GraphQL:
Yes, thank you. Please lint, update/write tests and add your name to the package.json file before you PR.
Semantic GraphQL is released under the MIT License.
GraphQL is released by Facebook, inc. under the BSD-license with an additional patent grant.