• REST APIs are restful APIs. Long Live GraphQL
  • By Samer Buna
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: sigoden
  • Proofread by: Jasonxia23, Shawnchenxmu

REST APIS are dead, GraphQL lives on

After years of using REST apis, when I first came across GraphQL and saw the problems it was trying to solve, I couldn’t resist giving this article its title.

Of course, in the past, this would have been an interesting experiment for me, but now I believe this interesting prediction is slowly happening.

Don’t get me wrong, I’m not saying GraphQL is going to kill REST or anything like that, REST will probably never really die, just like XML won’t really die. I just think GraphQL’s relationship to REST will become like JSON’s relationship to XML.

This article does not support GraphQL 100 percent. Note the overhead of GraphQL flexibility. Good flexibility often comes with high overhead.

I believe in “it all starts with questions,” so let’s get started.

To summarize: Why do YOU need GraphQL?

GraphQL nicely solves three important problems:

  • Multiple round trips to the data needed to populate a view: With GraphQL, we can always get all the initialized data to populate a view from the server in a single round trip. To achieve the same effect using REST apis, we need to introduce unstructured parameters and conditions that make management and maintenance difficult.
  • The client becomes dependent on the server: with GraphQL, the client has its own language: 1) it does not require the server to hardcode the structure and specifications of the data; 2) the client and the server are decouple. This means we can keep the client separate from the server and maintain and upgrade it separately.
  • Poor front-end development experience: With GraphQL, front-end developers use a declarative language to express their needs for the data needed to populate the user interface. They express what they need, not how to make it available. This creates a tight relationship between the data required by the UI and the data expressed by the developer in GraphQL.

This article elaborates on how GraphQL addresses these issues.

Before we begin, in case you’re not familiar with GraphQL at this point, let’s start with a simple definition.

What is GraphQL?

GraphQL is a language. If we teach GraphQL to an application, the application can declaratively communicate data requirements to the back-end data service that supports GraphQL.

Just as it’s easy for kids to learn a new language but hard for adults, it’s easier to write an application with GraphQL from scratch than to add GraphQL to a full-fledged application.

In order for the data service to support GraphQL, we need to implement a runtime layer and expose it to clients that want to communicate with the service. You can simply think of this layer added to the server as a GraphQL translator, or an agent representing the data service that speaks GraphQL. GraphQL is not a storage engine, so it cannot be used as a standalone solution. That’s why we can’t have a pure GraphQL service and need to implement a translation runtime.

This layer, which can be written in any language, defines a common graph-based template to publish the functionality of the data service it represents. Graphql-enabled clients can use this template to query as much as their capabilities allow. This strategy separates the client side from the server side, allowing both to be developed and extended independently.

A GraphQL request can be either a query (read operation) or a modify (write operation). In either case, the request is just a simple string with a specific format that the GraphQL server can parse, execute, and process. The most common response format in mobile and Web applications is JSON.

What is GraphQL? (Explain it to me as if I’m five years old)

GraphQL is all about data communication. You have a client and a server that need to communicate with each other. The client needs to tell the server what data it needs, and the server needs to return specific data based on the client’s needs. GraphQL acts as a middleman for this communication.

In the screenshot from my Pluralsight course — Building extensible apis with GraphQL, you ask, can’t a client communicate directly with a server? The answer is yes.

There are several reasons why we need to add a GraphQL layer between the client and server. One of the reasons, probably the main reason, is that it’s more efficient. Clients often need to fetch multiple resources from the server, and the server can usually only understand how to reply to a single resource. As a result, the client ends up making multiple round trips to the server to collect the data it needs.

With GraphQL, we can basically move this complex multiple request to the server and let the GraphQL layer handle it. The client makes a single request to the GraphQL layer and gets a response that perfectly matches the client’s needs.

There are many other benefits to using the GraphQL layer. For example, another big benefit is communicating with multiple services. When you have multiple clients requesting data from multiple services, the GraphQL in the middle simplifies and standardizes communication. While not a selling point compared to the REST API, which could easily do the same job, the GraphQL runtime provides a structured and standardized approach.

The screenshot shows my Pluralsight lesson — instead of having the client directly request two different data services (as shown in the slide), building an extensible API with GraphQL lets the client communicate with the GraphQL layer first. The GraphQL layer in turn communicates with two different data services. In this way, GraphQL solves the problem of clients having to communicate with multiple backends in different languages and converts a single request into multiple requests for multiple services in different languages.

Imagine that you know three people who speak different languages and have different fields of knowledge. Then imagine being asked a question that can only be answered by combining the knowledge of three people. If you have a translator who speaks all three languages, the task becomes to put the answers to your questions together, which is easy. That’s what the GraphQL runtime does.

Computers aren’t smart enough to answer any questions (at least not yet), so they have to follow some kind of algorithm. That’s why we need to define a template in the GraphQL runtime for clients to use.

This template is basically a functional document that lists all the questions that clients can query to the GraphQL layer. Templates have some flexibility in use because they use graphical nodes. The template also shows what the GraphQL layer can and can’t answer.

Still don’t understand? Let me describe GraphQL in the most precise and short terms possible: an alternative to REST API. Let me answer the question you’re likely to ask.

What’s wrong with REST apis?

The biggest problem with REST apis is their natural tendency to have multiple endpoints. This causes the client to make multiple round trips to get the data.

REST apis typically consist of multiple endpoints, each representing a resource. Therefore, when a client needs multiple resources, it needs to make multiple requests to the REST API to get the data it needs.

In the REST API, there is no language to describe client requests. The client has no control over what data the server returns. There is no language for the client to control the returned data. Rather, the number of languages a client can use is limited.

For example, there are REST apis that do reads like this:

  • GET /ResouceName– Gets a list of all records from the resource
  • GET /ResourceName/ResourceID– Obtains a specific record by ID

For example, the client cannot specify which fields to select from the resource’s record. Information exists only in the service that provides the REST API, and that service will always return all fields, regardless of what the client needs. To describe this problem, I borrow GraphQL terminology: over-fetching useless information. This wastes server and client network memory resources * Another big problem with REST apis is versioning. If you need to support multiple versions, you need to create multiple new endpoints for this purpose. This makes these endpoints difficult to use and maintain and, in addition, creates a lot of redundant code on the server side.

Some of the problems posed by REST apis listed above are problems that GraphQL tries to solve. That’s not the whole problem with REST apis, and I’m not going to try to explain what REST apis are and aren’t. I’m just talking about one of the most popular resource-based HTTP endpoint apis. These apis eventually become a combination of endpoints with regular REST features and special endpoints customized for performance reasons.

How does GraphQL work its magic?

There are many concepts and design strategies behind GraphQL, and here are some of the most important ones:

  • The GraphQL template is strongly typed. To create a GraphQL template, we need to define a few fields with types. These types can be raw data types or custom data types, and everything in the template requires a type. The rich type system brings rich features, such as API self-authentication, which allows us to create powerful tools for both the client and server side.
  • GraphQL organizes data in the form of graphs, which naturally form graphs. If you need a structure to describe data, diagrams are a good choice. The GraphQL runtime allows us to represent our data using a graph API that matches the natural graph structure of that data. -GraphQL has the nature of expressing data requirements declaration. GraphQL allows clients to describe their data requirements in a declarative language. This declarative nature leads to a mental model around GraphQL usage that is close to the way we think about data requirements in natural language, making it easier to use GraphQL than it would otherwise be.

The last concept is why I think GraphQL is a game changer.

These are all abstractions. Let’s dive into the details.

To solve the problem of multiple round-trip requests, GraphQL makes the response server an endpoint. Essentially, GraphQL takes the idea of a custom endpoint to its extreme by allowing that endpoint to respond to all data questions.

Another important concept that accompanies the concept of a single endpoint is the need for a powerful client request description language to communicate with a custom single endpoint. Without client request description language, a single endpoint is meaningless. It requires a language to parse custom requests and return data based on custom requests.

Having a client request description language means that the client can control the request. Clients can express exactly what they want, and the server can respond exactly what the client wants. This solves the overacquisition problem.

GraphQL provides an interesting workaround when it comes to versioning. Versioning can be completely avoided. Basically, we just need to keep the old fields and add new ones, and since we’re using a graph, we have the flexibility to add more nodes to the graph. Therefore, we can leave the old API on the diagram and introduce the new API without marking it as a new version. The API just adds more nodes.

This is especially useful on mobile because we can’t recharge the versions used on these mobile devices. Once installed, mobile applications can use older versions of the API for years. For the Web, we can easily control the API version by releasing new code, which is difficult for mobile applications.

Still not convinced? How about a one-to-one comparison between GraphQL and REST with an example?

REST style API vs GraphQL API — case study

Let’s say we’re developers building shiny new user interfaces to showcase Star Wars movies and characters.

The first UI we built was simple: an informational view showing individual Star Wars characters. For example, Darth Vader and other characters in the movie. This view needs to display the character’s name, year of birth, star name, and any titles that appear in any movie in which the character appears.

As simple as it sounds, we actually have to deal with three different resources: characters, planets, and movies. The relationship between the resources is simple, and anyone can easily guess the composition of the data here.

JSON data for this UI might look something like:

{" data ": {" person" : {" name ":" Darth Vader, "" birthYear" : "41.9 BBY", "planet" : {" name ":" Tatooine} ", "control-insistent pang" : [ { "title": "A New Hope" }, { "title": "The Empire Strikes Back" }, { "title": "Return of the Jedi" }, { "title": "Revenge of the Sith" } ] } } }Copy the code

Suppose the data service returns the data to us in the structure above. One possible way to render a view is to use react.js:

// The Container Component:
<PersonProfile person={data.person} ></PersonProfile>

// The PersonProfile Component:
Name: {person.name}
Birth Year: {person.birthYear}
Planet: {person.planet.name}
Films: {person.films.map(film => film.title)}
Copy the code

This is a simple example, but our experience with Star Wars also helps us to understand the relationship between UI and data. As expected, the UI uses all the keys in the JSON data object.

Let’s look at how to get this data through a RESTFUL API.

We need information about a single role, and assuming we know the role ID, REST-style apis tend to print this information:

GET - /people/{id}
Copy the code

This request will return the character’s name, year of birth, and some other information to us. A standard REST-style API would return the ID of our character’s planet and an array of the ids of all the movies that character has appeared in.

This request returns a response in JSON format similar to:

{" name ":" Darth Vader, "" birthYear" : "41.9 BBY", "planetId:" 1 "filmIds" : [1, 2, 3, 6], and other information we don't need * * * * * *}Copy the code

Then to get the name of the planet, we make a request:

GET - /planets/1
Copy the code

Then to get the title in the movie, we make a request:

GET - /films/1
GET - /films/2
GET - /films/3
GET - /films/6
Copy the code

When all six data are received from the server, we can combine them and generate the data needed for the view.

This is fine except for the fact that it takes six round trips to get the data needed for a simple UI. We explained how to get the data and how to process it to meet the needs of the view.

If you want to confirm what I’m saying you can try it yourself. There is a REST API service deployed on swapi.co/ that provides Star Wars data. Click on it and try to construct character data in it. The key names of the data may differ, but the API endpoints are consistent. You also need to make six API calls. Again, you have to get more information than the view needs.

Of course, this is just one implementation of the REST API, and there may be better implementations that make generating views easier. For example, if the API service supports resource nesting and understands the relationship between characters and movies, we can retrieve movie data in this way:

GET - /people/{id}/films
Copy the code

However, a pure REST API service is difficult to implement. We need to have the back-end engineers create custom endpoints for us. This contributes to the fact that REST apis continue to grow in size — we keep adding custom endpoints to meet the needs of ever-growing clients. Managing these custom endpoints is difficult.

Let’s take a look at the GraphQL strategy. GraphQL embraces the idea of custom endpoints on the server side and takes it to the extreme. The service would just be an endpoint and the channel would become meaningless. If we use HTTP implementation, the HTTP method will be meaningless. Suppose we have a single GraphQL endpoint with an HTTP address of/GraphQL

Because we want a round trip to get the data we need, we need to tell the server exactly what data we need. We use GraphQL to query:

GET or POST - /graphql? query={... }Copy the code

The GraphQL query is just a string, but it will contain all the data we need. That’s the power of the statement.

In English, we state data requirements like this: We need character names, birth years, planet names, and titles in all movies that appear. With GraphQL, we perform the following transformations:

{
  person(ID: ...) {
    name,
    birthYear,
    planet {
      name
    },
    films {
      title
    }
  }
}
Copy the code

Read the requirements in English again and compare them to the GraphQL query. They couldn’t get any closer. Now, compare the GraphQL query to the raw JSON data we started with. The GraphQL query corresponds exactly to the JSON data structure, but excludes all parts that are values. If we think of this situation in terms of a question-and-answer relationship, the problem is an answer primitive with no specific answer.

If the answer is:

The planet closest to the sun is Mercury.

A good way to ask a question is to leave it as it is and leave out the question:

Which planet has the closest sun?

The same relationship applies to GraphQL queries. Take the response data in JSON format, remove all the parts that are answers (objects that are values), and you end up with a GraphQL query that is perfectly suited to representing the question about the JSON response.

Now, compare the GraphQL query with the declarative React UI we used to show the data. All the data that appears in the GraphQL query appears in the UI. All the data that appears in the UI appears in the GraphQL query.

This is GraphQL’s powerful mental model. The UI knows exactly what data it needs, and it’s easy to extract the data it needs. Writing GraphQL queries becomes a simple task to extract as variables from the UI.

Flip the model, and it’s still powerful. If we know about GraphQL queries, we also know how to use that data in the UI. We don’t need to analyze the response data to use it, nor do we need documentation for these apis. It’s all built in.

Get Star Wars Data GraphQL hosted at github.com/graphql/swa… . Click in and try to construct the character data. With only a slight difference, which we’ll talk about later, here are the formal queries (using Darth Vader as an example) that can get the data needed for a view from this API

{
  person(personID: 4) {
    name,
    birthYear,
    homeworld {
      name
    },
    filmConnection {
      films {
        title
      }
    }
  }
}
Copy the code

This request returns our response data structure very close to what the view uses, and remember, we got the data from a round trip.

GraphQL flexibility comes with overhead

There is no perfect solution. GraphQL brings flexibility, as well as some clear questions and considerations.

GraphQL is more vulnerable to resource exhaustion attacks (denial-of-service attacks). The GraphQL server can be attacked with extremely complex queries that exhaust the server resources. It is easy to construct a chain of deeply nested relationships (user -> friends -> friends of friends). Or multiple queries for the same field through a field alias. GraphQL is not limited by exhaustion attacks, but we need to be careful when using GraphQL.

Here are some mitigation measures we can use. We can do some overhead analysis of advanced queries, placing some limit on the amount of data requested by a single user. We could also implement a mechanism to timeout requests that take a long time to process. Furthermore, given that GraphQL is just a processing layer, we can do rate limiting at the lower level below GraphQL.

If the GraphQL API endpoint we are trying to protect is not public and is only accessible internally by our private clients (Web, mobile), we can use a whitelist policy and pre-audit the queries that the server can handle. The client can only initiate an approved query to the server with a unique query ID. Facebook seems to have adopted this strategy.

When using GraphQL, we also need to consider authentication and authorization. Do we process GraphQL requests before, after, or in between?

To answer this question, think of GraphQL as your DSL (Domain-limited language) at the top of your back-end data request logic. It’s just a processing layer that we can put between the client and the actual data service (multiple).

Think of authentication and authorization as another layer of processing. GraphQL has little to do with the implementation of authentication and authorization logic. It’s not about that. But if we put these layers after GraphQL, we can use access tokens at the GraphQL layer to connect the client to the execution logic. This is similar to how we handle authentication and authorization in the REST style API.

Another task made more challenging by GraphQL is client-side data caching. Rest-style apis are easier to cache because they resemble directories. REST apis get data through access paths, which we can use as cache keys.

For GraphQL, we can use a similar strategy to use query fields as cache keys for response data. However, this approach is limited, inefficient, and prone to data consistency problems. The reason is that the results of multiple GraphQL queries can easily overlap, and this caching strategy doesn’t take into account this overlap.

There is a good solution to this problem. A graph query implies a graph cache. If we regularize the response data for a GraphQL query into a tiled set of records and give each record a globally unique ID, we can cache only those records instead of the entire response.

It’s not easy. This leads to some records pointing to other records, which leads to the possibility of managing a ring graph, which leads to traversal as we write and read from the cache, which leads to the need to write a layer to handle the cache logic. However, this approach is generally more efficient than response-based caching. Relay. Js is a framework that adopts this caching strategy and manages it automatically internally.

Perhaps the biggest concern for GraphQL is what is commonly referred to as an N+1 SQL query. GraphQL’s field queries are designed as separate functions, and fetching these fields from the database might result in a database query for each field.

Simple REST-style API endpoint logic, easy to analyze, easy to detect, can optimize SQL query statements to solve N+1 problems. GraphQL needs to handle fields dynamically, which is not easy to do. Fortunately, Facebook is working on a possible solution to a similar problem: DataLoader.

As the name implies, DataLoader is a tool that lets you read data from a database and make it available to GraphQL processing functions. Instead of fetching data directly from the database through SQL queries, we used DataLoader as a proxy to reduce the SQL queries we actually needed to send to the database.

DataLoader is implemented using a combination of batch processing and caching. If the same client request results in multiple requests to the database, DataLoader consolidates these problems and pulls the request data in batches from the database. The DataLoader caches this data at the same time and can retrieve it directly from the cache when subsequent requests require the same resource.


Thank you for reading this. If you find this article useful, click on the link below. Follow me for more node.js and JavaScript articles.

I created online courses at Pluralsight and Lynda. I of course includes the Advanced recently React. Js] (www.pluralsight.com/courses/rea)… , Advanced Node.js, and Learning full-stack JavaScript.

I’m also doing online and offline training for JavaScript, Node.js, React.js and GraphQL beginners to a higher level. If you are looking for a coach, please contact me. If you have questions about this and other posts I’ve written, find me on this Slack account (you can invite yourself) and ask questions at # Questions.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, React, front-end, back-end, product, design and other fields. If you want to see more high-quality translation, please continue to pay attention to the Project, official Weibo, Zhihu column.