GraphQL Best Practices
GraphQL is an open-source manipulation and data query language useful to APIs. It is a runtime that aids in fulfilling the queries of all existing data. Developed by Facebook in 2012, the language was released officially in 2015. When using this language, developers often wonder about GraphQL best practices. The following article takes a brief look at this concern. Listed below are some of the best practices when utilizing this language. Here are the best practices you will learn reading this article:
- Server-Side Caching and Batching
Ready to know more?
Generally served over HTTP through one endpoint, GraphQL expresses the service’s full set of capabilities. REST APIs, on the other hand, are different in their functionality. They expose a set of URLs, while each of these URLs exposes one resource each. While developers can use GraphQL alongside several resource URLs, doing so can make it more difficult to use with certain tools, such as GraphiQL.
Among the GraphQL best practices is the language’s JSON use with GZIP. Most GraphQL services normally respond through JSON, even though the spec for GraphQL does not require it. Some developers feel complacent about an API layer that promises enhanced network performance responding to JSON. However, since the language is mostly text, the resulting compression with GZIP is excellent. If you are a production GraphQL service, ensure you enable GZIP and always seek the following header from your clients. JSON’s popularity with clients and API developers is primarily due to the fact that it is ready to debug and read. A part of GraphQL syntax is inspired by JSON’s syntax.
Like other REST APIs, GraphQL service lacks any limitations against versioning. However, in most cases, the language avoids versioning. It does so by offering the tools required to implement a constant GraphQL schema update or evolution. Even though GraphQL prevents versioning, other APIs often welcome it. This happens due to the limited control over the data returned from the API endpoint. Any change while returning the data results in a breaking change and causes the formation of a new version. Due to the greater versioning of the API, developers are forced into a tradeoff, where they can either release new versions often or ensure the maintainability and understandability of the API.
GraphQL best practices prevent similar complexities. GraphQL only returns the data corresponding to your explicit requests. Therefore, you can implement changes or additions through specific types, thereby, preventing a breaking change and the formation of a new version. This extremely useful feature has effectively reduced the instances of breaking changes to such an extent that developers can create APIs without any versions.
Systems that identify ‘null’ generally offer both the common type and a nullable version of this type in the system. However, null is not included in the default types, unless you specifically declare it beforehand. One of the GraphQL best practices is that it includes a null option for all its default type systems. The reason for this inclusion is the fact that even the most well-laid plans can go awry in the backend of a networked service comprising databases and many other services.
For instance, a database may malfunction or go down, the system can throw an exception, an asynchronous action can fail and more. Apart from the simple system failures, authorization issues can also hamper your progress. In some cases, individual fields in a request may have varied authorization rules. With GraphQL, you can default each field to null, thereby, resulting in null returns for those specific fields instead of a request failure.
Another important part of GraphQL best practices is the presence of non-null variants. These types ensure that when clients request a return, the field never returns with ‘null’. In the event of an error, the previous parent field returns a null instead. While designing the GraphQL schema remember the various aspects that can go wrong during the process. Determine whether ‘null’ is an effective response to these issues. While in most cases, null is the appropriate value, sometimes you require a better value. You can use non-null types to classify such errors.
In GraphQL systems, some fields may return values in the form of lists. However, pagination for longer lists requires the input of the API developer. While you can choose from a range of different pagination designs for the API, each offers some advantages and disadvantages. Generally, GraphQL fields that can generate long lists accept arguments ‘first’ and ‘after’ to specify a certain range within the list. ‘After’, in such cases, is an important identifier of listed values.
When designing feature-heavy pagination for APIs, the best pattern, known as Connections is formed. Certain client tools within GraphQL, like Relay, are aware of these connections and can lend automatic support when a client’s pagination in GraphQL API utilizes this pattern.
Server-Side Caching and Batching
GraphQL allows a clean and simple interface for writing code on a server. All the fields on every type have only one function, which is to resolve the value for those specific fields. However, in the absence of additional considerations, GraphQL services may load an excessive amount of data from your databases. Often known as being excessively ‘chatty’, there is a simple workaround to deal with such issues.
Implementing a batching technique is one of the GraphQL best practices. In this method, multiple requests for data collection are grouped together over a short period. This group of requests is then transmitted as one request to an underlying microservice or database. The above-mentioned technique is easily achievable through special tools, such as DataLoader from Facebook.
Besides these practices, while using the GraphQL platform, you can also adopt many other effective techniques to make the development easier and more efficient. Some of these techniques include using a naming convention in your schema, nesting objects within queries, mutations resulting in affected objects, utilizing the input object type for mutations and much more.