GraphQL best practices for GraphQL schema design
Tags: webdev, graphql, node, react
Published: 2020-01-28 13:52:52

---
title: GraphQL best practices for GraphQL schema design
published: true
description: In this article we will go through various ways and basic patterns to help design schema in the best possible way. This is definitely not a complete guide; this is just a short list on how to avoid the biggest pitfalls that regularly come up. For more advanced info on designing your GraphQL schemas check out our newsletter. It is useful to approach schema design by making a check-list of what your schema should provide to guide you through the design process.
tags: webdev, graphql, node, react
canonical_url: https://atheros.ai/blog/graphql-best-practices-for-graphql-schema-design
---

This article is a part of <a title="GraphQL language course" href="https://graphqlmastery.com/course/graphql-language">free GraphQL Language course</a>. To fully understand the content you need to be familiar with basic GraphQL concepts like SDL language, GraphQL document syntax or <a href="https://atheros.ai/blog/input-object-type-as-an-argument-for-graphql-mutations-and-queries" title="Article on GraphQL input and output object types">GraphQL object types</a> and <a href="https://atheros.ai/blog/graphql-scalars-and-their-input-and-result-coercion" title="Article on GraphQL scalars">scalars</a>. If you do not have this knowledge, you can take a look at our older articles on <a href="https://atheros.ai/blog" title="Atheros blog">our older articles on atheros.ai</a> and on our <a href="https://medium.com/atheros" title="Atheros blog">medium publication</a>.

## Introduction

In this article, we will go through various ways and basic patterns to help design schema in the best possible way. This is definitely not a complete guide; this is just a shortlist of best practices and instructions on how to avoid the biggest pitfalls that regularly come up. For more advanced info on designing your GraphQL schemas check out our free <a title="GraphQL language course" href="https://graphqlmastery.com/course/graphql-language">GraphQL language course</a>. It is useful to approach schema design by making a check-list of what your schema should provide to guide you through the design process.

## Using input object type for mutations

It is extremely important to use just one variable for your mutations and use the input object type to simplify the structure of your GraphQL documents. In our schema, the example is our **createPlanet** mutation. In SDL this is written as follows:

``` graphql
type Planet implements Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime
  name: String
  description: String
  planetType: PlanetTypeEnum
}
input CreatePlanetInput {
  name: String!
  galaxyId: ID!
  description: String
}
type Mutation {
  createPlanet(input: CreatePlanetInput!): Planet!
}
```

We can see that we only have one argument input - this is typed as an input object type. We are applying a non-null modifier to ensure that the input payload cannot be null. To get more details on this pattern including why we should use it and how to implement it, you can follow <a title="Article on GraphQL input and output object types" href="https://medium.com/atheros/json-as-an-argument-for-graphql-mutations-and-queries-3cd06d252a04">this article on input object types</a> and this one on <a href="https://atheros.ai/blog/graphql-list-how-to-use-arrays-in-graphql-schema" title="GraphQL Lists and Non null modifiers">GraphQL lists and Non Null modifiers</a>.

## Returning affected objects as a result of mutations

When working with mutations it is considered good design to return mutated records as a result of the mutation. This allows us to update the state on the frontend accordingly and keep things consistent. To illustrate the concept let’s look at the following mutation:

``` graphql
type Mutation {
  createPlanet(input: CreatePlanetInput!): Planet!
  updatePlanet(input: UpdatePlanetInput!): Planet!
}
```

We can see that we return **Planet!** as a result of this mutation. This is then used by the cache and we can use it to update the store. In the **createPlanet** mutation we append the object to the list of planets. This normally needs to be done in the code. In the second example for **updatePlanet**, however, we can update the **Planet** automatically by its id.

## Using paginated lists by default

Paginated results are really important for security reasons and for ability to limit amount of records we would like to retrieve from the server. It is a good practice to structure paginated results as follows:

``` graphql
type PageInfo {
  endCursor: String
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
}
type ConstellationConnection {
  nodes: [Constellation]
  pageInfo: PageInfo!
  totalCount: Int!
}
```

This pagination is based on the so-called **cursor based pagination**. Whenever you fetch lists, I suggest you use paginated lists by default. You will avoid breaking changes of the schema in the future and it is almost always a much more scalable solution. You can also easily implement **amount limiting** and protect yourself against resource exhaustion attacks, where someone can query an overwhelming number of records from your database at once.

## Nesting your objects in queries

When building GraphQL schemas I often see faulty patterns which affect caching and hinder the efficiency of GraphQL queries on the client. If we rewrite our planet type with this bad practice we would write something like this:

``` graphql
type Planet implements Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime
  name: String
  description: String
  planetType: PlanetTypeEnum
  galaxyId: ID!
}
```

The problem here is the **galaxyId**. For the cache on the frontend, there will be inconsistencies. You need to think about the schema, reuse the types, and substitute these ids with the proper type. If you want to query for the **planet’s** constellation you need to do two queries. First, call the planet with the **galaxyId**, and then pass the **galaxyId** to an additional query. This is really inconvenient and does not maximize the power of GraphQL. In GraphQL it is much better to nest the output types. This way we can call everything with one single request and also perform caching and batching with <a title="Data loader" href="https://github.com/facebook/dataloader">data loader</a>.

``` graphql
type Planet implements Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime
  name: String
  description: String
  planetType: PlanetTypeEnum
  galaxy: Galaxy!
}
```

When we query for the **planet** and we want to include its **constellation** we can just call this GraphQL query:

``` graphql
query getPlanets {
  planets {
    nodes {
      id
      name
      constellation {
        id
        name
        createdAt
      }
    }
  }
}
```

## Elevating schema design with interfaces

The interfaces and unions are powerful tools for abstracting different concepts, reducing complexity and simplifying our schema. The best example for interfaces is the **Node Interface**. The Node interface is enforced by <a href="https://relay.dev/" title="Relay.js link">Relay.js</a>, but I would recommend implementing it to other schemas as well. Even if they do not use Relay.js on the frontend, it will still help to reduce complexity. To gain a more in-depth understanding of interfaces you can check out <a href="https://atheros.ai/blog/graphql-interfaces-and-unions-how-to-design-graphql-schema" title="GraphQL interfaces and unions">this article</a> or our <a title="GraphQL course" href="https://graphqlmastery.com/course/graphql-language">GraphQL language course</a>.

## Think about future schema changes

When implementing your initial schema, try to think about all possible future changes. In general, just invest in the schema. Think about where you can expect to add additional fields in the future and add output object types and input object types there so that you will not have to introduce breaking changes later on. If you are building a more complex app, just do not use GraphQL schema generators blindly. GraphQL schema generators are definitely useful in certain cases like prototyping or for instant high-performance APIs from the database, but in general, I would suggest to think about the schema design and tailor it to your frontend needs.

## Use consistent naming in your schema

It's really important to keep naming conventions consistent not just in GraphQL schema design. It is common good practice to use **camelCase** for your fields and **pascalCase** for the names of types. I find also useful to name for example input types like this:
``` bash
(action)(type)Input
```

**(action)** is **Create**, **Update** or **Delete** and **(type)** is the name of the type or entity, which is being updated. Although it is important to not get caught in designing schema in just simple crud operations, but tailor mutations to your frontend needs. We also need to ensure that some common patterns like pagination are named in the same manner. We use the **PageInfo** type and connection for the paginated list with nodes. Another good practice for enums is to use all capital letters for ENUM values, as they are basically constants.

>I hope that you liked this shortlist of best practices for GraphQL schema design. Feel free to ask any question and feedback at <a title="Contact email" href="mailto:david@atheros.ai"> david@atheros.ai</a>.