algonote(en)

There's More Than One Way To Do It

GraphQL Adoption Anti-patterns

Is GraphQL a silver bullet?

Introduction

In web and application development, there are various methods for passing data between servers and between server and clients.

The most common are SOAP and XML in the past. In most cases, JSON REST API is used recently. gRPC with Protocol Buffers is also used for microservices.

GraphQL is a language developed by Facebook for Web API. Its advantage is that it can retrieve data flexibly with a small number of requests. On the other hand, it is a tool specialized for some unique use cases, so I will discuss it here.

GraphQL basic structure

Let's compare JSON API with GraphQL.

JSON REST API example

GET /posts/1

request params

なし

response

{
  "user": {
    "name": "user1"
  }
  "title": "title1",
  "body": "body1"
}

PATCH /posts/1

request params

{
  "user_id": 123,
  "title": "title1",
  "body": "body1"
}

response

{
  "status": "success",
  "data": {
    "user_id": 123,
    "title": "title1",
    "body": "body1"
  }
}

GraphQL example

Query

request

query {
  posts(id: 1) {
    user {
      name
    }
    title
    body
  }
}

response

{
  "data": {
    "posts": [
      {
        "user": {
          "name": "user1"
        }
        "title": "title1",
        "body": "body1"
      }
    ]
  }
}

Mutation

request

mutation {
  updatePost(id: 1, title: "title1",  body: "body1") {
    user {
      name
    }
    title
    body
  }
}

response

{
  "data": {
    "updatePost": {
      {
        "user": {
          "name": "user1"
        }
        "title": "title1",
        "body": "body1"
      }
    }
  }
}

Simple comparison

While the REST API has HTTP verb types such as POST, GET, PUT, PATCH, and DELETE, GraphQL is organized into two types: query, which is equivalent to reading, and mutation, which is equivalent to writing. GraphQL over HTTP is treated internally as a POST-only json request.

While the requestor is not allowed to change the response specification in detail in most JSON APIs, GraphQL allows the requestor to specify the fields to see. If the schema is defined in advance, as GraphQL stands for, it is possible to pull in any number of related models like liked list. You can acquire all the necessary data at once with a single request.

Like JSON, it is text data, so binary data needs to be encoded with base64 or other encoding to be sent.

Anti-pattern: Use GraphQL without considering compatibility with error and monitoring tools

GraphQL is based on a different paradigm compared with REST API. If you do not consider compatibility with error tools, all errors will be treated as GraphQLError caused by POST /graphql. It means that settings in the language's library is necessary for meaningful errors.

Also, since GraphQL can go deeper and deeper into the graph. It may be difficult to limit the load on the server side when heavy requests are thrown. It is client side dependent. Since some REST API calls means one API call in GraphQL, the load is not measured by the number of API requests per hour.

Query complexity limit and stored procedure-equivalent Persisted Queries can be used to mitigate the complexity of queries, but they require different know-how than the REST API.

Anti-pattern: Use GraphQL in backend-heavy team

One of the advantages of GraphQL is that the client can flexibly change the display contents without waiting for changes on the server side. This is the exact opposite of Server-Driven UI, where the UI can be controlled on the server side by prior agreement.

GraphQL has the aspect that a certain ORM layer is transferred from the backend to the client. Since the complex processing cannot be absorbed in the server side, if a large number of members in your team is server-side, they may have to wait client-side implementation. This could slow down the development speed.

Also, if GraphQL is used for a service that has iOS and Android (and SPA), you will naturally have to write twice the same code for iOS and Android. Using cross-platform tools such as ReactNative, Flutter, or Kotlin Multiplatform is better for GraphQL. If your web service has the complex processing on the backend, you should consider whether you will be totally happy with the client side duplication.

Anti-pattern: Connect raw tables to GraphQL without consideration

GraphQL has use cases like no-code. For example, Graphile and Hasura are tool for that case. Use GraphQL without backend development during the initial phase is one way.

It may need more settings than Firebase. However, since it is based on PostgreSQL, you can get option to retrieve data by simple SQL later. You can freely perform complex processing on the back-end later.

It's not just about those easy-to-use tools, but if you connect any bare tables to GraphQL, you have the risk of being vulnerable to schema changes Change sometimes means maintenance.

In the Netflix case, it seems that they created a View and connected GraphQL to it, so you should think carefully about change tolerance.

Anti-pattern: Use GraphQL for non-CQRS architecture system

GraphQL has query and mutation, read and write as the first basic category.

The MVC framework used by small and medium teams as a web backend architecture basically uses the same DB and the same model for both writing and reading. On the other hand, the systems of a certain scale sometimes adopt the CQRS architecture, which separates DB and system flow by write and read operations.

I think there's a law of clean implementation when the design of the Web API is consistent with the design of the infrastructure. GraphQL starts from Facebook, a large web service. GraphQL is more suitable with the system that Read/Write is completely separated like CQRS. As Rails proves, the ActiveRecord pattern shines for small and probably medium team. If your service is large and plain MVC is a pain, GraphQL may be a good option anyway.

There is an architecture called GraphQL Federation. That is another GraphQL use case. In Backend for Frontend architecture, we have to write custom implementation on aggregation server. By using GraphQL Federation as a replacement for the custom aggregation server, we can remove some implementations.

Anti-pattern: Use GraphQL for all APIs

As seen above, my opinion is that GraphQL is not a silver bullet. It depends on your use cases. In that sense, it is a good strategy to partially adopt GraphQL in your APIs, rather than just using 0 or 1.

If you need a solid implementation such as authentication that does not need for flexibility, a REST API is sufficient. If you need an API for screens that are frequently A/B tested, GraphQL fits.

Because of its flexibility, there are cases where it is used only for admin pages.

Future Work

If you find errors, please let me know.

Next? gRPC?