DEV Community

Peter Nycander
Peter Nycander

Posted on • Originally published at hubburu.com

Almost 4 Years in - Common GraphQL design mistakes I've seen

When I have consulted companies implementing GraphQL I tend to see the same design mistakes pop up quite often. This article will go through a few of the lessons I have learned after almost four years of GraphQL design, so that you don't have to repeat those mistakes.

Adding the field type: String

This might not seem obvious to everyone that this is a mistake, but it is a clear code smell. GraphQL is based around its typesystem, so using type: String instead of the built-in __typename just does not take advantage of that fact. What you want in most cases is an interface. Each possible value of type: String gets its own GraphQL type which implements that interface.

This will allow you to make a smarter schema, because odds are that not every field is shared among all the different types. Another benefit is that reading the schema will hint to the consumer what possible values to expect. The hinting can be achieved with an enum, but there is little to no downside to using the interface instead.

Mirroring the backend models

It is quite easy when starting with GraphQL to just expose all the fields that you get from the backend services or database models. The point of GraphQL is to let clients pick and choose from the data, right? Well kind of, but you should think before exposing too much data.

The best way is usually to go client first. Ask yourself the question: What do the consuming clients actually need? It could be that you have a Country object with fields such as countryCode, fullName, currency etc, but all the frontend needed was the url and alt text to a flag of that country. The client would probably have to look at countryCode to generate a url, and hope they always match, and concatenate fullName with some other string to build the alt text.

This way of designing is often referred to as a demand-oriented schema. Demand-oriented schema design deserves its own article, because it is a fascinating subject. But the big reasons to use it is that 1. You will design a schema more aligned with the product rather than the database, and 2. this will decouple your database design from your GraphQL schema (which might useful when you change database design later on).

Limiting the API to suit your caching requirements

A normal way to deal with high volumes of traffic is to cache requests using HTTP caching, which can be leveraged in browsers and CDNs (Content Delivery Networks). To get a decent cache hit ratio you need to make sure that the resources are the same for every user. You would not want the request to get the users avatar or username to get cached and being served to the wrong user!

GraphQL is most commonly used through HTTP POST requests, which are usually not cached. However there are ways to send GraphQL requests using HTTP GET, either with the full query string or using persisted queries.

In a typical GraphQL schema you usually have a mix of private and public data. The public data is cachable, but the private is not. This creates a problem if you want to leverage HTTP caching. You can really only cache fully public data.

Optimizing for cache hits would however lead to trying to reuse the same exact query in multiple places, and instead of having private data like progressPercentage: Float of a type Movie you would have to have a separate query to get the progress, and try to stitch the data together after the fact. This is what we are used to doing with REST apis, and we are really not getting the full benefits of GraphQL by doing this.

I would recommend going as long as you can with just using caching inside of your server logic using something like redis, and only consider HTTP caching as a last resort when designing your GraphQL api.


If you liked this article, I have a few more design mistakes listed in the original article at: https://www.hubburu.com/articles/common_graphql_design_mistakes

Discussion (0)