GraphQL Best Practices
First and Foremost
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. It provides a complete and understandable description of the data in your API as well as gives clients the power to ask for exactly what they need and nothing more.
It was developed by Facebook as an internal solution for their mobile apps and was later open-sourced to the community.
If you look at the official GraphQL Best Practices page, you will notice that it only shares brief descriptions of some of the more common best practices, which in my opinion are just some guidelines and has no backing implementation details. I will try to share some concrete implementations for a backend application using GraphQL.
This is definitely not a complete guide; this is just a list of best practices and instructions on how to avoid the biggest pitfalls that regularly come up.
Use Lean Data Models For Simple Queries And Mutations
It can be tricky to design data models when you start creating your schema. There are multiple ways for you to do that and while most of the implementations would work fine, the problems only surface when you try to extend your implementation.
In general, it is always good to take a look at platforms that are already using GraphQL. Github API is a good place to understand how the input and output objects are modelled and how the queries and mutations are exposed.
As a general rule of thumb. Keep your mutations as small as possible. Keep inputs lean and with elaborate naming.
addComment is simple, concise, and clear. It takes in an input
AddCommentInput and returns a
Comment back. We are applying a non-null modifier (!) to ensure that the input payload cannot be null. One thing to note here is that we send back the created/updated object. This allows the client to update the state so the user knows if something is updated.
Use Nested Objects To Reduce Network Calls
Comment in the previous example was a simple type with only one filed. Let's say we want to send back the
userId as part of the
Comment. One possible approach would be this.
While this might seem like a quick and easy approach, there is a small problem. The
userId in itself would not be very useful for the frontend, it would eventually have to make a call to fetch the user with the
userId. 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 data loader.
The approach would be to send
User back in the
Comment. It would look something like this.
The query then looks something like this.
Enable Proper Error Handling
This is one of the things that almost everybody forgets in the beginning and then when the application becomes huge, it becomes very cumbersome to cover the error cases. And when it comes to GraphQl error handling can be pretty tricky, as the response always has an HTTP status
200 OK. If a request fails, the JSON payload response will contain a root field called
errors that contains the details of the failure.
Without proper error handling, the response will be something like this:
This isn’t helpful at all — the response doesn’t tell you what actually went wrong. And this is why most of the applications fail without actually giving users the right message or a fallback option. Handling errors is a big topic in itself and I have this written separate blog for that with a full running example. That can be found here.
[## Error Handling With GraphQL, Spring Boot, and Kotlin
Modeling GraphQL errors and exceptions using Spring Boot with Kotlin
In the nutshell the output after you implement that will look like this:
Use Interfaces and Unions For Abstraction
Interfaces serve as parent objects from which other objects can inherit.
Interface type extensions are used to represent an interface which has been extended from some original interface. For example, this might be used to represent common local data on many types, or by a GraphQL service which is itself an extension of another GraphQL service.
And a union is a type of object representing many objects.
GraphQL Unions represent an object that could be one of a list of GraphQL Object types, but provides for no guaranteed fields between those types. They also differ from interfaces in that Object types declare what interfaces they implement, but are not aware of what unions contain them.
Consider the following example. The profile is an interface that is implemented by
User as well as
Company. Few fields are mandatory. Additional fields can be added according to the varying type and requirements.
And similar Unions can be used when you have a query that needs to return two types. Your
search query can return either
User or a
For more elaborate explanations and implementation, you can read this.
[## Using GraphQL With Spring Boot: Interfaces and Unions
GraphQL’s interfaces and unions provide a good way to handle multiple field types in queries
Use Fragments To Reuse Defined Types
Fragments are reusable things in GraphQL. You can assume fragments like functions in a programming language.
Fragments are consumed by using the spread operator (
...). This will reduce a lot of redundant code.