Core concepts in GraphQL

Arun Rajeevan
5 min readMar 7, 2019

--

The Schema Definition Language (SDL)

GraphQL has its own type system that’s used to define the schema of an API.

Here is an example how we can use the SDL to define a simple type Person

type Person {
name: String!
age: Int!
}

This type has two fields, they’re called name and age and are respectively of type String and Int.

Note :The ! following the type means that this field is required.

It’s also possible to express relationships between types.

type Book{
title: String!
author: Person!
}

Generally, a schema is simply a collection of GraphQL types. However, when writing the schema for an API, there are some special root types:

type Query { ... }
type Mutation { ... }
type Subscription { ... }

The Query, Mutation, and Subscription types are the entry points for the requests sent by the client. To enable the allPersons-query that we saw before, the Query type would have to be written as follows:

type Query {
allPersons: [Person!]!
}

allPersons is called a root field of the API. Considering again the example where we added the last argument to the allPersons field, we’d have to write the Query as follows:

type Query {
allPersons(last: Int): [Person!]!
}

Putting it all together, this is the full schema for all the queries and mutation that you have seen in this chapter:

type Query {
allPersons(last: Int): [Person!]!
}
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
type Subscription {
newPerson: Person!
}
type Person {
name: String!
age: Int!
posts: [Post!]!
}
type Post {
title: String!
author: Person!
}

For example, consider the following query to get user with id “abc”

query {
user(id: "abc") {
id
name
}
}

Fetching Data with Queries

GET request

When receiving an HTTP GET request, the GraphQL query should be specified in the “query” query string. For example, if we wanted to execute the following GraphQL query:

query
{
{
me {
name
}
}
}

This request could be sent via an HTTP GET like so:
http://myapi/graphql?query={me{name}}

POST request

A standard GraphQL POST request should use the application/json content type, and include a JSON-encoded body of the following form:

{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}

operationName and variables are optional fields. operationName is only required if multiple operations are present in the query.

Response

Regardless of the method by which the query and variables were sent, the response should be returned in the body of the request in JSON format. As mentioned in the spec, a query might result in some data and some errors, and those should be returned in a JSON object of the form:

{
"data": { ... },
"errors": [ ... ]
}

If there were no errors returned, the "errors" field should not be present on the response.

Writing Data with Mutations

Next to requesting information from a server, the majority of applications also need some way of making changes to the data that’s currently stored in the backend. With GraphQL, these changes are made using so-called mutations. There generally are three kinds of mutations:

  • creating new data
  • updating existing data
  • deleting existing data

Mutations follow the same syntactical structure as queries, but they always need to start with the mutationkeyword. Here’s an example for how we might create a new Person:

mutation {
createPerson(name: "Bob", age: 36) {
name
age
}
}

Realtime Updates with Subscriptions

Another important requirement for many applications today is to have a realtime connection to the server in order to get immediately informed about important events. For this use case, GraphQL offers the concept of subscriptions.

When a client subscribes to an event, it will initiate and hold a steady connection to the server. Whenever that particular event then actually happens, the server pushes the corresponding data to the client. Unlike queries and mutations that follow a typical “request-response-cycle”, subscriptions represent a stream of data sent over to the client.

Here’s an example where we subscribe on events happening on the Person type:

subscription {
newPerson {
name
age
}
}

After a client sent this subscription to a server, a connection is opened between them. Then, whenever a new mutation is performed that creates a new Person, the server sends the information about this person over to the client:

{
"newPerson": {
"name": "Jane",
"age": 23
}
}

GraphQL Server Basics: GraphQL Schemas, TypeDefs & Resolvers

The GraphQL schema provides a clear contract for client-server communication.

The GraphQLSchema object is the core of a GraphQL server.

For the example above, the GraphQLSchema object looks as follows:

const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString }
}
})
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQLID }
}
}
}
})
})

Resolvers implement the API

In its most basic form, a GraphQL server will have one resolver function per field in its schema. Each resolver knows how to fetch the data for its field. Since a GraphQL query at its essence is just a collection of fields, all a GraphQL server actually needs to do in order to gather the requested data is invoke all the resolver functions for the fields specified in the query.

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQLID }
},
resolve: (root, args, context, info) => {
const { id } = args // the `id` argument for this field is declared above
return fetchUserById(id) // hit the database
}
}
}
})
})
const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: {
type: GraphQLID,
resolve: (root, args, context, info) => {
return root.id
}
},
name: {
type: GraphQLString,
resolve: (root, args, context, info) => {
return root.name
}
}
}
})
  1. root : the result of the previous call (initial value is null if not otherwise specified).
  2. args: This argument carries the parameters for the query
  3. context: An object that gets passed through the resolver chain that each resolver can write to and read from (basically a means for resolvers to communicate and share information).
  4. info: An AST(Abstract Syntax Tree) representation of the query or mutation.

--

--

No responses yet