A Different Api Design Approach

Facebook developed GraphQL to rebuilt its iOS and Android apps. What is GraphQL? Is it an alternative to RESTful API or could it be replaced with it in the future?

In 2012, Facebook decided to rebuilt its iOS and Android applications as native.

Since they started off with the motto "Write once, run anywhere", they ran into a lot of problems. As the application gets more complicated, they've had performance issues and crashes. For that reason, they needed a different API approach which leaded them to develop GraphQL and they released it in 2015. Well, let's see what is GraphQL and if it can replace the most commonly used RESTful API in client and server communication.

GraphQL offers a good way to design and use APIs. Whether it's an alternative to RESTful or could it be replaced with it in the future, we will see. But it is possible to say that GraphQL has already been used by many companies. The most well-known companies among them are Facebook -of course- along with Twitter, Airbnb, Github and Pluralsight.

We also use graphQL in the company I work for. One of the reasons we chose GraphQL was that there are too many endpoints in the REST structure and if the client wants to get certain data, a new endpoint has to be created again. This situation can get more and more complex in large-scale applications.

GraphQL enabled us to get rid of complex endpoints and access the desired data on the client-side allowing us to speed up things with ease of use and maintenance as well as better developer-friendly codes.

REST API VS GRAPHQL API

Untitled.png

Based on the picture above, both of them primarily use the HTTP protocol to create APIs.

REST is an architecture style for designing networked applications. GraphQL is a query language developed to improve performance and usability.

Data fetching: I think this point is the most interesting feature of GraphQL. Because in the REST API, we may have to send requests to too many endpoints in order to get the data we want. GraphQL provides a single endpoint to access the data on the server.

In the RESTful API, I assume that when you send a request to the server through the client, we know that it returns an object or send an object according to the type of the request (get, post, put, delete). This is where the GraphQL client-side comes into play. When you make a request, you can specify the object you want and which properties of that object you want to access, and you can make requests accordingly. Since the server creates a design according to it, it responds to what you want.

Versioning: Whenever there is a change in the REST API, you use the versioning method to indicate those changes. For this, you may need to check frequently the API you use. GraphQL links the required and requested information so that it is included when changes occur.

Error Management: You can manage errors by examining HTTP status codes in REST API. But in GraphQL you will always see 200 (OK) status and you may have trouble managing errors. By using GraphQLError, you can define errors and trigger these errors in resolvers. You can also return the errors array in your responses by using Apollo-Server.

Document: While creating the REST API, you need to create the API documentation by yourself. With Swagger, you can create a configuration and prepare your documentation. But in GraphQL you don't need to prepare documentation. GraphiQL provides docs and playground environment for you. It creates a diagram and documentation with the queries you write, and you can run the query you want with the playground it provides and see what it returns.

Caching: We can only "POST" requests from the client to the server in GraphQL, we may have difficulties in caching. Because we are sending a POST request, the proxy cannot analyze this request. All we have to do is sending the information into the body. Separating the client and the server can also be an alternative to solve this problem.

If you are developing applications using the React library, you can use open-source frameworks such as Apollo GraphQL (Data Graph Platform) to solve problems like caching and error management.

Apollo can simplify communication between server and client providing you with easier solutions instead of using Redux while making state management, as well as allowing you to write less code. If you want to examine there is also Relay as an alternative to Apollo.

Now let's deep dive into GraphQL and take a look at how it is used.

GraphQL SDL

Defining the schema with GraphQL SDL (GraphQL Schema Definition Language) helps you to create the docs and the schema presented by GraphQL or GraphQL Playground, and to better understand the document.

schema {
query: Query
mutation: Mutation
}

Data Types

GraphQL also has 5 built-in scalar types: Int, Float, String, Boolean and ID. Of course, we are not limited to these 5. It can also be field type, object type and enumeration type.

GraphQL Components

It allows us to make separate definitions on client and server sides in our project. On the client-side, these are Queries, Mutation, Fragment. On the server-side, it is possible to separate them into Type Definition, Query Definition, Mutation Definition, Resolvers, Composition and Schema.

Supposing that we are making a Todo application, let's see how we define GraphQL components.

GraphQL supports languages such as Scala, JavaScript, Ruby and many more. Here I will develop the project with JavaScript.

Type Definition

// Server
var { buildSchema} = require('graphql');
var schema = buildSchema (`
  type Todo {
  id: String
  text: String
  completed: Boolean
}
`);

Here, we can define the schema definition and define the object and its properties to be included in the schema. Thus, we enable GraphQL to prepare more reader-friendly docs.

Queries

//Client
query todos {    
  todos {      
   id
   text
   completed    
  }  
 }

We have defined the objects and properties we want to get from the server. Under the object of Todos, we actually say that "I want this information of that Todo object".

Mutations

// Server
type Mutation  {    
 createTodo(text: String!): String    
 removeTodo(id: String!): String    
 updateTodo(id: String!): String  
}

We made our definition of mutation on the server-side. We can think of it as create, update, delete operations in Mutation Rest API. Also, you can create accounts, generate tokens or get your data from input.

We specified our mutations on the client-side. Pay attention to the exclamation mark at the end of String!. It means we expect the server to return a non-empty value. If it returns empty, execution error would be triggered.

Resolvers

const resolvers = {  
Query: {    
 todos: () => todos  
},
 Mutation: {    
   createTodo: (parent, args, context, info) => {      
    return todos.push({        
    id: Date.now().toString(),        
    text: args.text,        
    completed: false      
  });    
 },    
removeTodo: (parent, args, context, info) => {      
 for (let i in todos) {        
  if (todos[i].id === args.id) {          
       todos.splice(i, 1);        
     }      
  }      
 return args.id;    
},    
updateTodo: (parent, args, context, info) => {      
  for (let i in todos) {        
    if (todos[i].id === args.id) {          
        todos[i].completed = !todos[i].completed;        
       }      
  }      
  return args.id;    
}  
}};

In Resolvers, we perform our operations for each component we define in the diagram. It can be used to send data to the resolvers database, to access the data or to make requests to the third-party APIs.

Fragments

// Client
fragment Daily on Todo {
  text
  completed
}

It is used to define data pieces that you may want to use more than one.

// Client
query GetTodo {
todo(id: "3"){
 ...Daily
 issueImage(size: LARGE)
 }
}

As understood from the code above, we can define the data we want to use in more than one place as a fragment and then use it wherever we want with the spread operator (…) in JavaScript.

Subscribers

It is used to establish a real-time connection with the server. In most cases, it enables the client to receive certain data in real-time. It is a very useful feature especially for stock exchange or instant messaging applications.

// Server
type Subscription {
 newTodo: Todo
}

In our scenario, let's say we want to receive the added new Todos instantly. With the subscription we created, we create the WebSocket connection so that the server can send updates to the client continuously.

// Client
subscription {
 newTodo {
  id
  text
 }
}

Directives

It allows us to organize our queries and create conditionals according to the parameters received. We create directives by defining them with the @ sign.

query todos($days: Boolean) {    
  todos {      
   id
   text
   ucText: text @uppercase  // text'in büyük harf dönmesini sağlar.
   days @skip(if: $days) //eğer false ise days döner
   days @countinue (if: $days) // eğer true ise days döner
  }  
 }

We learned the information we need to know on the basis of GraphQL.

Let's try sending requests with GraphQL using the Rick and Morty API.

I developed this project with React.js, so I created a project with create-react-app. I decided to use Apollo Client on the front-end of this project.

Let's include GraphQL and Apollo client in our project.

npm install graphql --save
npm install @apollo/client graphql

We normally use state management to manage data. Redux, MobX, Context API and Recoil are state management libraries for React. But Apollo Client has management that will help us manage the data we will use, and apollo-link-state will exactly provide us with that. But I used apollo-boost in the project. Apollo boost includes apollo-link-state, but it allows us to transmit data to client state instead of transmitting it over the link.

Apollo Client 3 came out this July and some improvements have been made. For example, I read that it handles in itself without downloading apollo-link-state into your project. For further details, check the link below: [https://www.apollographql.com/docs/link/links/state/#with-apollo-boost](https://www.apollographql.com/docs/link/links/state/#with-apollo-boost](https://www.apollographql.com/docs/link/links/state/#with-apollo-boost)

Now let's run this command in our project:

npm i apollo-boost
//index.js

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "@apollo/react-hooks";
// Apollo Client 3 de ApolloProvider apollo/client içinde geliyor. 
// Yukarıdaki @apollo/react-hooks a ihtiyacınız yok.
// import { ApolloProvider } from "@apollo/client" bu şekilde import edebilirsiniz.

const client = new ApolloClient({
  uri: "<https://rickandmortyapi.com/graphql/>"
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById("root")
);

serviceWorker.unregister();

Here we see index.js in the project created with create-react-app. We're importing ApolloClient and ApolloProvider.

Rick and Morty API supports GraphQL, so we can send a request to the URL it gave us thanks to Apollo Client.

Provider will allow us to send the response from the API we request to our React components. As Apollo has in its own document, this ApolloProvider has a client props and we connect our external API to this props.

Let's go to App.js, which we wrapped with ApolloProvider.

There is a playground in here, you can learn about the API by checking the document and schema created by GraphQL.

// App.js
import React from "react";import "./App.css";
import { useQuery } from "@apollo/react-hooks";
import { gql } from "apollo-boost";
//import { gql, useQuery } from '@apollo/client'; 
//Apollo Client 3 de bu şekilde kullanabilirsiniz.

const rickandmorty = gql`
  {
    characters {
      results {
        id
        name
        image
        gender
        species
      }
    }
  }
`;

function App() {
  const { loading, error, data } = useQuery(rickandmorty);
  if (loading) return <p>Loading...</p>;
  if (error) return <p> Oppss !!</p>;
  const newData = data.characters.results;
  return (
    <div className="container">
      <h1 className="title">Rick and Morty API with GraphQL</h1>
      {newData.map((item) => {
        return (
          <div key={item.id} className="card">
            <img src={item.image} alt="RickandMorty" />
            <div className="container">
              <div className="container-title">
                <h3 style={{ width: "150px" }}>
                  <b>{item.name}</b>
                </h3>
                <p>{item.species}</p>
                <p>{item.gender}</p>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}
export default App;

The useQuery we import enables us to run the queries we have defined, and at the same time returns loading, error, data objects that we can use in the client.

GQL, on the other hand, is being used to create our query; then we fill the fields we want to include in the payload.

Later, we take the information we defined on the query side from the data returned and we print it on the screen.

Actually sending requests to the external API is that simple. GraphQL and Apollo handled most of the things we had to do.

If you want to check the source code;

https://github.com/Ulubayam/apolloClient

When we run the project, we expect such a page to appear:

https://rickandmortywithgraphql.herokuapp.com/

You can also review the source codes of the Todo application I mentioned above. It was developed by using Apollo Client and Apollo Server.

https://github.com/Ulubayam/TodoAppWithApolloClient

For GraphQL Server, there is also graphql-yoga as an alternative to Apollo Server. You can check it if you want:

https://github.com/prisma-labs/graphql-yoga

Conclusion

To summarize, according to the researches made by most companies using GraphQL, they stated that GraphQL accelerated the development process, that their projects could scale faster, their performance increased, they encountered fewer errors and the architecture became healthier.

If we talk about the disadvantages, the ecosystem is new and developing. If you are going to install a caching system, you have to use a client library and if you have a distributed structure on the server-side, things may become more complicated for you.

You can also subscribe to GraphQL's weekly newsletter:

https://www.graphqlweekly.com/

Resources

https://www.apollographql.com/

[graphql.org/graphql-js/] (graphql.org/graphql-js)

https://blog.graphqleditor.com/why-you-should-try-graphql/