GraphQL and DQL schemas
The first step in mastering DQL in the context of GraphQL API is probably to understand the fundamental difference between GraphQL schema and DQL schema.
In GraphQL, the schema is a central notion.
GraphQL is a strongly typed language. Contrary to REST which is organized in terms of endpoints, GraphQL APIs are organized in terms of types and fields. The type system is used to define the schema, which is a contract between client and server. GraphQL uses types to ensure Apps only ask for what’s possible and provide clear and helpful errors.
In the GraphQL Quick start, we have used a schema to generate a GraphQL API:
type Product {
productID: ID!
name: String @search(by: [term])
reviews: [Review] @hasInverse(field: about)
}
type Customer {
username: String! @id @search(by: [hash, regexp])
reviews: [Review] @hasInverse(field: by)
}
type Review {
id: ID!
about: Product!
by: Customer!
comment: String @search(by: [fulltext])
rating: Int @search
}
The API and the engine logic are generated from the schema defining the types of objects we are dealing with, the fields, and the relationships in the form of fields referencing other types.
In DQL, the schema described the predicates
Dgraph maintains a list of all predicates names with their type and indexes in the Dgraph types schema.
Schema mapping
When deploying a GraphQL Schema, Dgraph will generates DQL predicates and types for the graph backend.
In order to distinguish a field name
from a type Person
from the field name
of different type (they may have different indexes), Dgraph is using a dotted notation for the DQL schema.
For example, deploying the following GraphQL Schema
type Person {
id: ID
name: String!
friends: [Person]
}
will lead the the declaration of 3 predicates in the DQL Schema:
Person.id default
Person.name string
Person.friends [uid]
and one DQL type
type Person {
Person.name
Person.friends
}
Once again, the DQL type is just a declaration of the list of predicates that one can expect to be present in a node of having dgraph.type
equal Person
.
The default mapping can be customized by using the @dgraph directive.
GraphQL ID type and Dgraph uid
Person.id is not part of the Person DQL type: internally Dgraph is using uid
predicate as unique identifier for every node in the graph. Dgraph returns the value of uid
when a GraphQL field of type ID is requested.
@search directive and predicate indexes
@search
directive tells Dgraph what search to build into your GraphQL API.
type Person {
name: String @search(by: [hash])
...
Is simply translated into a prediate index specification in the Dgraph schema:
Person.name: string @index(hash) .
Constraints
DQL does not have ‘non nullable’ constraint !
nor ‘unique’ constraint. Constraints on the graph are handled by correctly using upsert
operation in DQL.
DQL queries
You can use DQL to query the data generated by the GraphQL API operations. For example the GraphQL Query
query {
queryPerson {
id
name
friends {
id
name
}
}
}
can be executed in DQL
{
queryPerson(func: type(Person)) {
id: uid
name: Person.name
friends: Person.friends {
id: uid
name: Person.name
}
}
}
Note that in this query, we are using aliases
such as name: Person.name
to name the predicates in the JSON response,as they are declared in the GraphQL schema.
GraphQL Interface
DQL does not have the concept of interfaces.
Considering the following GraphQL schema :
interface Location {
id: ID!
geoloc: Point
}
type Property implements Location {
price: Float
}
The predicates and types generated for a Property
are:
Location.geoloc: geo .
Location.name: string .
Property.price: float .
type Property {
Location.name
Location.geoloc
Property.price
}
Consequences
The fact that the GraphQL API backend is a graph in Dgraph, implies that you can use Dgraph DQL on the data that is also served by the GraphQL API operations.
In particular, you can
- use Dgraph DQL mutations but also Dgraph’s import tools to populate the graph after you have deployed a GraphQL Schema. See GraphQL data loading
- use DQL to query the graph in the context of authorization rules and custom resolvers.
- add knowledge to your graph such as meta-data, score, annotations, …, but also relationships or relationships attributes (facets) that could be the result of similarity computation, threat detection a.s.o. The added data could be hidden from your GraphQL API clients but be available to logic written with DQL clients.
- break things using DQL: DQL is powerful and is bypassing constraints expressed in the GraphQL schema. You can for example delete a node predicate that is mandatory in the GraphQL API! Hopefully there are ways to secure who can read/write/delete predicates. ( see the ACL) section.
- fix things using DQL: this is especially useful when doing GraphQL Schema updates which require some data migrations.