GraphQL schemas for a service are now most often specified using what’s known as the GraphQL SDL (schema definition language), also sometimes referred to as just GraphQL schema language. It’s a language with a very simple syntax that allows to define a schema very succinctly. Once you have a gasp at the different syntax elements of the SDL, you’ll be able to write schemas in no time flat.
Here’s what a basic GraphQL schema deffinition for a simple todo app could look like:
# Enumeration type for a level of priority
enum Priority {
LOW
MEDIUM
HIGH
}
# Our main todo type
type Todo {
id: ID!
name: String!
description: String
priority: Priority!
}
type Query {
# Get one todo item
todo(id: ID!): Todo
# Get all todo items
allTodos: [Todo!]!
}
type Mutation {
addTodo(name: String!, priority: Priority = LOW): Todo!
removeTodo(id: ID!): Todo!
}
schema {
query: Query
mutation: Mutation
}
A lot is going on here, so let’s break down some of the most important things:
Object types are specific to a GraphQL service, are defined with the type keyword and start with a capital letter by convention. They define the type name and the fields present under that type. Each field in an object type can be resolve to either other object types or scalar types. Scalar types point to actual data and represent the leaves of the graph.
In the above schema deffinition we have the Todo object type as well as the Query and Mutation root object types. Only the Query root type is required in all GraphQL schemas, but the mutation root type will most often also be present when the service allows for updating, adding or deleting data. Additionally, a Subscription root type is also available, to define operations that a client can subscribe to.
There are 5 built-in scalar types with GraphQL: Int, Float, String, Boolean and ID. Scalar types, as opposed to object types, point to actual data. The ID type resolves to a string, but expects a unique value.
Enumeration types allow to define a specific subset of possible values for a type. In the previous example, the Priority enum type can take a value of LOW, MEDIUM or HIGH and anything else will result in a validation error. On the client strings are used to provide a value for an enum type.
As you can also see from the above example, modifiers can be used on the type that a field resolves to by using characters like ! and […]. Here’s a breakdown, using the String scalar type as an example:
Comments are added with the # symbol and only single-line comments are allowed.
It’s also possible to define custom scalar types with a syntax like this:
scalar DateTime
With this though, the GraphQL service will need to define how the custom scalar is to be serialized and validated.
Union types define a type that can resolve to a number of possible object types:
# ...
union Vehicule = Car | Boat | Plane
type Query {
getVehicule(id: ID!): Vehicule!
}
With union types, on the client, inline fragments have to be used to select the desired fields depending on what subtype is being resolved:
query {
getVehicule {
...on Car {
year
}
...on Boat {
color
}
...on Plane {
seating
}
}
}
Interfaces are somewhat similar to union types, but they allow multiple object types to share some fields:
interface Vehicule {
color: String
make: String
speed: Int
}
type Car implements Vehicule {
color: String
make: String
speed: Int
model: String
}
# ...
Each type that implements an interface need to have fields corresponding to all the interface’s fields, but can also have additional fields of their own. This way, on the client, inline fragments can be used to get fields that are unique to certain types:
graphql {
getVehicule {
color
make
...on Car {
model
}
}
}
When a query or mutation expects multiple arguments, it can be easier to define input types where each field represents an argument:
# ...
input NewTodoInput {
name: String!
priority: Priority = LOW
}
type Mutation {
addTodo(newTodoInput: NewTodoInput!): Todo!
removeTodo(id: ID!): Todo!
}
There’s also a syntax to add human-readable documentation for types and fields, which can become really helpful when using a tool like GraphiQL or GraphQL Playground to browse the documentation for a schema.
Let’s take our initial todo schema example and add some documentation for the types and some of the fields:
"""
Priority level
"""
enum Priority {
LOW
MEDIUM
HIGH
}
type Todo {
id: ID!
name: String!
"""
Useful description for todo item
"""
description: String
priority: Priority!
}
"""
Queries available on the todo app service
"""
type Query {
"""
Get one todo item
"""
todo(id: ID!): Todo
"""
List of all todo items
"""
allTodos: [Todo!]!
}
type Mutation {
addTodo(
"Name for the todo item"
name: String!
"Priority levl of todo item"
priority: Priority = LOW): Todo!
removeTodo(id: ID!): Todo!
}
schema {
query: Query
mutation: Mutation
}
As you can see, documentation for types or fields is added by wrapping with the *“”" … “”“* syntax. The *” … "* is used for documentation on arguments.
With this you should be off to the races with defining GraphQL schemas. You can also always refer to the official spec when you’re unsure about some of the syntax.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.