GraphQL vs JSON:API for e-commerce

GraphQL is hot these days! More and more GraphQL APIs appear and especially in the Javascript / NodeJS scene, GraphQL is extremely popular. As progressive web applications using GraphQL are also on the rise (not only in the e-commerce area), we compare how GraphQL and the JSON REST API standard fits for e-commerce applications based on our experience in these areas:

  • Ease of implementation and use
  • Performance and scalability
  • Reading and writing data

GraphQL

The GraphQL specification was invented by Facebook because they need to retrieve data efficiently by web clients to render their frontend. Facebook accounts and their relationships to other accounts are a graph they need to query, hence the name “Graph Query Language”. The clients should be able to do complex queries while minimizing the data in the response to the really required properties only.

Advantages

One API for all client requirements

There’s only one API endpoint that can handle all client requests. Internally, the requests for different resources may be routed to different APIs so a GraphQL server can act as a director for different services. For the clients this may be easier to work with.

Efficient requests including related resources

With one request, you can not only retrieve the data of the requested resource. Instead, you can also fetch all related resources that are referenced by the requested resource entries. This reduces the number of required requests to retrieve all data to exactly one.

Retrieve only the data you really need

For each resource and related resource, only the required properties can be retrieved. Thus, the amount of data transfered over the network is reduced to the absolute minium that is required by the client.

Writing several (related) resources at once is possible

Creating and updating resources and related resources can be done in only one request. This means, saving data isn’t limited by the network but only by the server processing the requests.

Exact schema definition is provided by the server

A GraphQL API requires a schema that exactly specifies the resources, its properties and types as well as what queries can consist of. This enables built-in introspection and schema validation on the client and server.

type Product {
  id: ID
  label: String
  status: Boolean
}

Well defined query language

The specification tells you exactly how queries look like, independent of the concrete GraphQL API implementation:

{
  product(code: '1000.001') {
    label
    attribute(type: 'variant') {
      id
      name
  }
}

Client side libraries for working with the API available

There are Javascript libraries available for working with GraphQL APIs easily, the most widely used one is Apollo. Contrary to the simple JSON:API this is absolutely necessary because its hard to implement a GraphQL API by hand.

Disadvantages

No caching by CDNs or proxies possible

All requests are sent as POST request in the body of the request. This makes it impossible for intermediate caches to store those requests and return the response immediately. Thus, scaling a GraphQL API is only possible by adding more servers.

Optimizing queries can be hard because they are not fixed

Each client can send arbitrary queries to the GraphQL server. They can be different in the fields requested or related resources as well as the used filters so it’s difficult to create indexes for the database tables.

Clients can build deep and complex queries overloading the server

As servers don’t implement a fixed list of queries, malicious clients can build deeply nested queries that doesn’t use the indexes of the database. This can lead to a denial of service attack easily.

Client and server are thighly coupled how they work together

Whenever an item in the backend is refactored or its properties renamed, all clients consuming the GraphQL API has to be updated. To avoid that you need to keep the original data structure forever and only adding new items and properties but that will bloat your backend code of the time.

Application state and what’s possible next must be programmed into the client

Clients can use all kinds of queries to fetch data from the server but they don’t get any information what they should do with that data. Developers must hard-code the next steps in the clients, e.g. how to fetch the data for the next list page or how to add the product to the basket.

Query language adds more complexity

The GraphQL query language is more complex than using a simple URL with some parameters. Instead, you have to write a complex query even if you only want to retrieve all products with their names, e.g.

curl -X POST -H 'Content-Type: application/json'
    -d '{ "query": "{ products { name } }" }'
    http://localhost/graphql

Client side caching necessary which may introduce additional points of failure

Because caching by CDNs and proxies is impossible, the GraphQL server only has limited resources and network latency is bad for the user experience, you have to implement caching in the client. This can add another source of problems to your software stack that’s hard to debug.

‍JSON:API

The JSON:API standard was invented by Ember.js, one of the first major Javascript frameworks. It’s main goal is to reduce the amount of client code required for using a server API that communicates in a well defined way. Implementation on the client and server side should be easy and use existing best practices.

Advantages

Efficient requests which can include related resources

Usually, one one request is needed to fetch all required data from the server because you can use the “include=text,price,media” parameter to fetch all listed resources too.

Fetch only required data

You can pass the “fields” parameter to fetch only the properties you need from the server just like you can using GraphQL. This also works for related resources:

http://localhost/jsonapi/product?fields[product]=id,label&fields[text]=langid,content

Excellent caching by CDNs, proxies and browsers

REST APIs perfectly fit to the current internet infrastructure. The browser and intermediate servers automatically cache responses so services offered via a JSON:API can scale indefinitely for read heavy applications.

Client and server are decoupled for independent evolution

Request don’t map directly to the entries in the (NoSQL) database, so you can change the backend and data structures while still maintaining compatibility to clients implementing a previous version of the API.

Application state can be managed by the server

The JSON:API standard includes a “links” section which contains links for the next possible actions like moving forward to the next page or for adding a product to the basket. Thus, the client doesn’t have to know how to build them on its own.

"links": {
    "self": "http:\/\/localhost\/jsonapi\/product\/13",
    "next": "http:\/\/localhost\/jsonapi\/product\/21",
    "basket\/product": "http:\/\/localhost\/jsonapi\/basket\/default\/product"
}

Queries can be optimized for the required use cases

In contrast to GraphQL, you have to think about the use cases for your JSON:API upfront because clients can’t ask for something there’s no resource endpoint for. In terms of optimization possibilities that’s a huge advantage because you know what kind of queries you can expect.

Interacting with the API is plain simple

There’s no query language involved for simple requests and returning the list of products with their name is only a matter for using:

curl http://localhost/jsonapi/product?fields=label

Disadvantages

Only single resource (database record) can be updated at once

Write heavy applications where also related resources must be updated (think about e.g. an administration interface) are not suited well for a REST API. You can only update one resource per request which can require multiple requests necessary if you want to save data.

No specification of the query language so every implementation has it’s own

There’s a “filter=…” parameter defined in the JSON:API standard but not the query language behind due to different requirements. This leads to custom implementations for each API but client developers may need to handle several query languages in one client.

No schema returned by default, has to be documented elsewhere

For the returned items and properties, there doesn’t exist a common schema description including the data types. Developers need to know what the server will return.

Schema validation must be done yourself

The APIs implementing the JSON:API standard are not self descriptive and you need to manually verify that the request and response matches the documented properties and types.

Conclusion

There are some good reasons why to use GraphQL but it’s not a replacement for all JSON REST API as some are stating with “GraphQL is taking over APIs”.

The best use case for GraphQL according to our observations is for internal APIs where the tight coupling between client and server isn’t a big problem. They can also act perfectly as a single endpoint for clients. Futhermore, you should think about access control to the GraphQL API because it’s inherently prone to denial of service attacks. The lack of scalability is a good reason to hide it from the public too.

APIs based on the JSON:API standard are better suited “for the wild” than GraphQL according to the current state. They enable clients and servers to evolve independently and can also reduce code in the clients. When leveraging CDNs and set cache timeouts correctly, JSON:APIs can scale infinitely. Only for applications with many writes to several resources at once they are not suited well.

Leave a Reply

Your email address will not be published. Required fields are marked *