GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.
In this article, we’ll look at the GraphQL type system and how it describes what data can be queried.
Schemas and Types
GraphQL requests are about selecting fields on objects.
In a query, we have a root object, then we select a field from that and fields below that level if it exists all the way to the bottom level.
For example, if we have the query:
{
person {
name
}
}
Then we get something like:
{
"data": {
"person": {
"name": "Jane"
}
}
}
as the response.
A GraphQL query closely matches the result so we can predict what the query will return without knowing that much about the serve.
But it’s still useful to have an exact description of data we can ask for.
This is where explicit types are useful.
Type language
GraphQL services can be written in any language. Therefore, types have to be defined in a language-agnostic way.
Object types and fields
We can define a GraphQL data type as follows:
type Person {
name: String!
addresses: [Address!]!
}
In the code above, we created a new type with the type
keyword. In the type, we have the name
field, which is String
, and addresses
, which is an array of Address
es, which is another type.
String
is a built-in scalar type. The exclamation marks mean the field is not nullable. Therefore, String!
is a non-nullable string.
[Address!]!
is an array of Address
objects. It’s also non-nullable, so we can always expect an array with zero or more items when we query for addresses
. Since Address!
is also non-nullable, we can always expect every item of the array to be an Address
object.
Arguments
Every field of a GraphQL object type can have zero or more arguments.
For example, we can write the following:
type Person {
name: String!
height(unit: HeightUnit = METER): Float
}
In the code above, we have the height
field in the Person
that takes a unit
as the argument. unit
is of type HeightUnit
which is set to METER
by default.
height
returns a Float.
The Query and Mutation types
There’re 2 types that are special within a schema. They’re Query
and Mutation
.
Every GraphQL service has a query
type and may or may not have an mutation
type.
These types are the same as any other object type, but they’re special because they define the entry point of every GraphQL query.
If we have a query like the following:
query {
person {
name
}
robot(id: "2000") {
name
}
}
Then the GraphQL service needs to have the query type with the person
and robot
fields:
type Query {
person(name: String): Person
robot(id: ID!): Robot
}
We define mutation types the same way. We define fields on the Mutation
type and those are available as the root mutation fields we can call in our query.
Scalar types
Scalar fields are what all object types contain in the end. They’re the most basic types of fields.
GraphQL comes with the following scalar types out of the box:
Int
— a signed 32-bit integerFloat
— a signed double-precision floating-point valueString
— a UTF-8 character sequenceBoolean
—true
orfalse
ID
— a unique identifier that used for fetching an object or as the key for a cache. It’s serialized the same way as a String but it’s not intended to be human-readable
Most GraphQL service implementations also let us specify custom scalar types.
We can define one as follows:
scalar Date
Then it’s up to use to decide how to serialize, deserialize and validate them.
Enumeration types
Enum types are special types of scalar that are restricted to a particular set of allowed values.
It lets us validate that any argument of the type is one of the allowed values.
Also, it communicates through the type system that a field will always be one of a finite set of values.
We can define an enum as follows:
enum Fruit {
APPLE
ORANGE
GRAPE
}
Then if we have Fruit
, we expect it to be either APPLE
, ORANGE
or GRAPE
.
Conclusion
We can define types so that we can validate the data that’s submitted to the GraphQL server.
To do this, we create a type with the type
keyword if it’s an object type.
We can also create scalar types which are the most basic types that can be included in other types or referenced directly. We can define them with the scalar
keyword.
To define a type with fixed values, we can define an enum
type with a few possible constant values.
The exclamation mark indicates that a type is not nullable.