Ask a Question

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.