Manually registered GraphQL schemas

Hi!

Stardog is a great product and I love the attention to UX! I do have some questions regarding the graphql implementation, specifically wrt manually registered GraphQL schemas.

  • In the docs, "Only the parts of the graph defined in the schema will be exposed to the user" is listed as one of the advantages of having a custom schema. However, how can you manage what graphql apis are exposed? The automatic one seems to be always on.
  • Probably related: is there a way (possibly with directives) to map graphql types or properties server-side, instead of in the query (eg. by using @withPrefixes?) Another post seems to suggest this was added to a prior release, but the docs don't mention this. Having this client-side is not a huge problem, but it does require a lot of knowledge about the server-side.
  • When I switch on the automatic schema generation, it generates a schema that uses the @iri directive, which doesn't seem to be documented. I suppose it is used to indicate that an graphql type corresponds with a particular RDF type, but it doesn't seem to work when I try to apply it in a custom schema. It reminds me of the hypergraphql approach, which is very intuitive IMO.

Thanks!

Best,

Miel

Hi Miel,

I'm glad to hear you like Stardog, Let me try to answer your questions:

  • The default schema is generated only if graphql.auto.schema option is set to true (which is the default) and no default scheme has been registered. So you can disable this option or register a default schema manually using the command stardog graphql schema --add default starwars starwars.graphqls as explained at the end of this section.

  • You are right that documentation is missing information about this directive. This directive is actually designed to do what you are asking in your third question. You can specify the namespace or the full IRI of a type or a property in the schema using this directive. There are couple different ways you can use this directive:

  1. If you want to concat the name to a namespace you can do:
type Document @iri(namespace: "http://xmlns.com/foaf/0.1/")

This will make this type to correspond the IRI http://xmlns.com/foaf/0.1/Document.

  1. If you already defined a prefix for that namespace you can use the prefix directly:
type Document @iri(prefix: "foaf")

Assuming the prefix is mapped to the same namespace, the end result is same as the previous case.

  1. If you want to rename a prefix but use a different name in GraphQL you can do:
type Doc @iri(prefix: "foaf", localname: "Document")

The IRI is still same as before but in GraphQL the type name is Doc (this is like a built-in alias).

And this directive can be used with any field as well. Prefixes used for the type do not get inherited by the fields so you would need to repeat these definitions at every level. This gives you a chance to mix and match namespaces as in this example:

type Document @iri(prefix: "foaf") {
	label: String! @iri(prefix: "rdfs")
	date: String @iri(prefix: "dc")
	topic: String @iri(prefix: "foaf", localname: "primaryTopic")
}

We will update the documentation to include this information. Thanks for pointing this out.

Best,
Evren

Hi Evren,

Thanks for the clarification; this makes a lot of sense. Some follow-up questions if I may :blush::

  • how does this work with interfaces in the graphql schema? Do implementation inherit the namespaces?
  • When I add a @iri(prefix: "foaf") to my type, do all properties automatically map to foaf unless stated differently?
  • is the IRI of the resource the thing you type as ID! ? Or is it always the property iri as a reserved keyword? it's not entirely clear because the filter is called id.
  • related to the previous question: Can I use the query Human(iri: "http://example.org/humans/1") { ... } to get a single resource, or how can I achieve this?
  • can I somehow limit the source of a graphql type or schema to a named graph? Similar to the @config(graph: Human) you can put in the query?
  • can a database only have a single graphql api? Or can you host one for a single schema for example?

@evren btw, I can't get this to work.

Consider this simple graphql schema configured as default schema:

schema {
    query: QueryType
}

type QueryType {
    Organization(id: ID, orderBy: ID): Organization
}

interface IOrganization {
  label: String!                                  
  altLabel: String                                                                 
}

type Organization implements IOrganization        @iri(namespace : "http://www.w3.org/ns/org#")  {
  label: String!                                  @iri(namespace : "http://www.w3.org/2004/02/skos/core#", localname: "prefLabel")
  altLabel: String                                @iri(namespace : "http://www.w3.org/2004/02/skos/core#")                                
}
  • When graphql.auto.schema is false, the schema does not work and I can only use graphql with queries like
{
  org_Organization {
    skos_prefLabel
  }
}

Is this expected?

  • When graphql.auto.schema is true, the queries above don't work anymore can I can use graphql with queries like
{
  Organization {
    label
  }
}

However, no data is returned because the sparql query is

SELECT *
FROM <tag:stardog:api:context:local>
{
?0 rdf:type :Organization .
?0 skos:label ?1 .
}

So the property mapping of label is picked up, but the type mapping Organization is not.

Hi Miel,

First, looks like a I made a mistake in my first message when I described the schema annotations. These annotations should be defined on fields (e.g. under QueryType) and not on the type definition. Second, the localname attribute can only be used with prefix but not namespace. That is why in the SPARQL query you are getting skos:label and not skos:prefLabel. So you need to change that definition to be either:

@iri(prefix : "skos", localname: "prefLabel") 

to use the already registered skos prefix or use the full IRI of the property int he directive:

@iri(value : "http://www.w3.org/2004/02/skos/core#prefLabel") 

So the final schema should look like this:

schema {
    query: QueryType
}

type QueryType {
    Organization(id: ID, orderBy: ID): Organization @iri(namespace : "http://www.w3.org/ns/org#")
}

interface IOrganization {
  label: String!                                  
  altLabel: String                                                                 
}

type Organization implements IOrganization {
  label: String!                                  @iri(value : "http://www.w3.org/2004/02/skos/core#prefLabel")
  altLabel: String                                @iri(namespace : "http://www.w3.org/2004/02/skos/core#")                                
}

Once you registered a default schema manually the value of graphql.auto.schema does not matter. But if you are querying without the schema (i.e. without the --schema option in the CLI) then the registered schema will not have an effect either.

To answer your other questions:

  • There is no inheritance of namespaces in the schema. They need to be specified for each field separately.
  • We use the built-in GraphQL type ID to represent IRIs. There is no id field by default. That property exists in the starwars example but it is just a regular property.
  • You can use iris filter to get a single resource but since the type of iri is an ID you cannot pass a string as in your example. You can do Human(iri: Alice) where the IRI will be resolved to the default namespace in the database or Human(iri: ex_Alice) to resolve it for the namespace ex.
  • Named graphs cannot be specified in the GraphQL schema, only in the queries. This would be a useful extension.
  • There can be one or more GraphQL schemas for each database. Each schema can expose a different subset of the underlying graph resulting in different APIs.

Best,
Evren

1 Like

Hi Evren,

That seems to work, thanks! And thanks for your many answers! I assume that the word QueryType is what makes Stardog know its querying a type?

Second, the localname attribute can only be used with prefix but not namespace. That is why in the SPARQL query you are getting skos:label and not skos:prefLabel.

Clear! It feels a bit odd though. Given that prefix works, it would make sense that @iri(namespace : "http://www.w3.org/2004/02/skos/core#", localname: "prefLabel") would work too. Or at least not a huge addition to a new release?

There is no inheritance of namespaces in the schema. They need to be specified for each field separately.

Inheritance makes no sense indeed if annotations should be defined on fields only. Out of curiosity, why did you choose not to have a Type mapping?

You can use iris filter to get a single resource but since the type of iri is an ID you cannot pass a string as in your example. You can do Human(iri: Alice) where the IRI will be resolved to the default namespace in the database or Human(iri: ex_Alice) to resolve it for the namespace ex.

I understand the choice (you want to stay within the graphql typing), but it's unfortunate, because the client 1) needs to know what namespaces are, and 2) what namespaces to use! That's the whole reason you would use schema annotations such as @iri

BTW, if you have a '-' in your namespace (which is allowed in RDF syntaxes), the graphql gives you Invalid Syntax : offending token '-' when executing for example Human(iri: ex-org_Alice)

Named graphs cannot be specified in the GraphQL schema, only in the queries. This would be a useful extension.

Cool! I hope this makes it to a release.

There can be one or more GraphQL schemas for each database. Each schema can expose a different subset of the underlying graph resulting in different APIs.

But how do you query them in isolation? Is this the <database>/graphql/<schema>? And can I put individual access control on any of these?

On final question: GraphQL APIs are optional by default, ie. non-null has to be indicated explicitely with !. So why does the @optional exist? Or is a switch like graphql.default.nullable: true an option?

Hi Miel,

You are right supporting localname with prefix should be easy. I think annotations on types should be doable as well.

You are also right about using <database>/graphql/<schema> for querying with a specific schema. It is also possible to send the query to <database>/graphql/ with a @schema: schemaName variable in the query request. Stardog itself does not provide any access control mechanism for using GraphQL schemas.

Optionailty is one area where our implementation differs from the GraphQL spec. In our implementation all fields are required by default and then you specify optionally via the @optional keyword in the query. An configuration option to control if fields should be optional or not by default makes sense.

Best,
Evren

Hi Evren,

Check, things seem to work well for now, thanks for the help.

It seems your spambot has hidden my last message for some reason, so I can't see what I typed :wink:
One thing though: there is an bug with using a dash in graphql, which is prevents you from refering to any prefix or URI that contains a -. For example, the Human(iri: ex_Alice) you mentioned will throw a syntax error if you have Human(iri: ex_Alice-smith) or Human(iri: ex-test_Alice).

Best,

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.