Techwondoe

The API Conundrum : REST or GraphQL.

API Conundrum
February 3, 2021
We are all familiar with the concept of evolution; just as how humans have developed and evolved, so has technology.

I myself have lived through the time when dotcom was a bubble to a time when cryptocurrency is a bubble. Just in those three decades, you can think how fast and rapidly technology has changed. I can go on and on and talk about life before 1990 to life after 2020 but this is not what this blog post is about, this blog post is about one specific aspect that has evolved over time i.e the API consumption architecture.

For readers who are not so familiar with what APIs are, API or Application Programming Interface in simplest terms can be thought of as an interface to interact with our software. As per a recent study, 55% of companies are using APIs for building B2B products, 36% for mobile products, 29% for B2C/consumer products, 26% for employee productivity, and 22% for IoT applications. More information around this study can be found here.

People who are a bit familiar with APIs would have come across these popular jargons

· Simple Object Access Protocol (SOAP)

· Representational State Transfer (REST)

· Graph Query Language (GraphQL)

Now there is no actual direct comparison between the three as one is a protocol (SOAP), one is an architecture style and one is a query language but somehow as part of evolution, they are looked upon as Gen X, Millennials, Gen Z as specified by the listing order. If you want to further read about SOAP or CORBA or even older legacy systems, you can find tons of information online about them online. Let us now talk about the two main jargon which constitutes this post.

RESTful APIs: The Popular Kid

REST or Representational State Transfer is an architectural style for an API that uses HTTP(s) request to access and use data.

A RESTful API—also referred to as RESTful Web Service or REST API is an API that conforms to the constraint of REST architectural style.

For an API to be considered RESTful, it has to conform to these criteria:

· A client-server architecture made up of clients, servers and resources with requests managed through HTTP

· Stateless client-server communication, which means no client information is stored between request

· A REST API should be designed to store cacheable data.

· A unified interface that permits autonomous development of the application

· A REST API should include several layers that operate together to construct a hierarchy that helps generate a more scalable and flexible application.

Even to date, REST is the most popular and widely accepted way of designing APIs. But since 2015 when Facebook decided to open-source GraphQL, many new companies and even old ones have started migrating towards GraphQL. But why the sudden shift? Well before we jump into that let us first understand briefly what GraphQL is.

GraphQL: New Kid On The Block

GraphQL is a query language for the API that prioritizes giving back clients exactly the data they request and no more. GraphQL came to life to solve problems that were found with REST APIs

  1. Over-fetching or Under-fetching: With REST API if you do a GET request, theusual RESTful API would give you back the entire payload unless you put in application logic to minimise that, GraphQL solves this problem by making the client request for exactly what they need nothing more, nothing less.

  2. Endpoint For Everything: With RESTful API we usually have 4 endpoints supported for 4 different operations and we have the same pattern across every resource. With GraphQL you just have one endpoint for all your queries.

  3. Lesser Bandwith: With REST as we give back a high surplus of data, it consumes a lot more bandwidth, but with GraphQL that problem goes away as you decide the amount of data you need.

Code Example

Now that we know what REST is and why GraphQL came to fruition. Let us quickly go through a code example and see how can one easily create APIs with either of them.

Initial Setup

Let us start by setting up our database, if you don’t have a local setup, you can simply run the command below to spin up a MySQL docker instance

dockerrun --name some-mysql -e MYSQL_ROOT_PASSWORD=password -p 3306:3306 -d mysql;

Next, let us set up the schema, you can simply do that by using the file provided in the repository.

cd database
docker exec -i some-mysql mysql -uroot -ppassword <<< $(cat ./ddl.sql)

As we are using TypeORM in our example with synchronize:true. Our table will get auto-created based on our entity. This brings me to the next thing, let’s set up our entity.

import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class User {
  @PrimaryGeneratedColumn("uuid")
  id: string;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  username: String;
}

Create RESTful APIs

Let us start by setting up a simple REST API using the most popular stack these days i.e

· Typescript

· TypeORM (using MySql DB)

· Express

Now as we are building a standard REST API, all we need is to define our routes

app.get("/users", async function(req: Request, res: Response) {// logic goes here});app.get("/users/:id", async function(req: Request, res: Response) {// logic goes here});app.post("/users", async function(req: Request, res: Response) {// logic goes here});app.put("/users/:id", async function(req: Request, res: Response) {// logic goes here});app.delete("/users/:id", async function(req: Request, res: Response){// logic goes here});

To start the server, we need to tell our express server to listen onto a specific port, which can be done by adding the following command to our code

// start express serverapp.listen(3000);

If you wish to test out your application you can run the following curl command

//Create User
curl --location --request POST 'http://localhost:3000/users' \
--header 'Content-Type: application/json' \
--data-raw '{
"firstName" : "Utsav",
"lastName" : "Sharma",
"username":"utsav.sharma"
}'
---------------------------
//Get Users
curl --location --request GET 'http://localhost:3000/users'
---------------------------
//Get User By Id
curl --location --request GET 'http://localhost:3000/users/0c8c8a3f-211a-4ffc-a179-3c70e1888b7b'
---------------------------
//Delete User
curl --location --request DELETE 'http://localhost:3000/users/0c8c8a3f-211a-4ffc-a179-3c70e1888b7b'

Couple of things to notice

1. We had to create 4 different endpoints to support one resource, Imagine if you had 8–10 different resources you would have had to create 32–40 different endpoints based on your requirements.

2. The client had no say in the amount of data they wished to receive. We spurted out our entire entity even when half of it may not be needed by the client.

Create GraphQL APIs

For our migration, we will use Apollo which is the most popular graphql server. Also, it provides a package to integrate Apollo server into an (existing) Express application.

Let us start by adding in the required packages

npm install apollo-server-express graphql npm install @types/graphql --save-dev

There are few key aspects to understand while setting up graphql API''s

· Schema Types: Your GraphQL schema defines what types of data a client can read and write to your database. Schemas are strongly typed, which unlocks powerful developer tooling. The structure of your schema should derive from what your client needs

const typeDefs = gql\`  # Your schema will go here //This syntax is also called SDL (schema definition language)\`;

· Object Types: Object type is defined to represent an object that an application client might need to interact with. Most of the declaration made inside schema type is actually object type. A simple object type would look something like this

type User {  id: String!  firstName: String!  lastName: String!  username: String!}

· Query Types: Now using object type we have defined the object that exists in our database, but we haven’t provided a way yet for the client to fetch that data. To help with that aspect we use query type. As part of this blob, you define the supported queries the client can interact with. E.g

type Query {users: [User]! //Here i am telling if the query is for users, get all users    user(id: String!): User //Whereas here I am telling when I pass an Id fetch me a user that it belongs too
}

· Mutation Type: Now query helps to fetch data but if the requirement is to add or modify data, you would use the mutation type. For e.g below declaration would instruct the client to use addUser to add a new user

type Mutation {addUser(firstName: String!, lastName: String, username: String): String!  }

· Resolver: A resolver is a logical representation of types. If types is declaration, then resolver is the actual implementation. For this example, my query resolver and mutation resolver would look something like this

export const resolvers = {
  Query: {
    user: async (_: any, args: any) => {
      const { id } = args;
      return await User.findOne({ where: { id: id } });
    },
    users: async (_: any, args: any) => {
      return await User.find();
    },
  },
  Mutation: {
    addUser: async (_: any, args: any) => {
      const { firstName, lastName, username } = args;
      try {
        const user = User.create({ firstName, lastName, username });
        await user.save();
        return user.id;
      } catch (error) {
        return null;
      }
    },
  },
};

Once you have both resolver and types in place, all you are left to do is start your graphql server, which can be done using the following snippet

    const startServer = async () => {    const server = new ApolloServer({ typeDefs, resolvers });    await server.start()    await createConnection();    const app = express();    server.applyMiddleware({ app });    app.listen({ port: 4000 }, () =>        console.log(\`style='font-family:"Segoe UI Emoji",sans-serif;mso-bidi-font-family:"Segoe UI Emoji"'>🚀 Server ready at http://localhost:4000\${server.graphqlPath}\`)    );};

Notice that we have just one endpoint now i.e http://localhost:4000/graphqlinstead of 4 different endpoints which we had with REST API.

Now let’s say for instance I want to add a new user, I can do that using the following mutation

curl --location --request POST 'http://localhost:4000/graphql' \--header 'Content-Type: application/json' \--data-raw '{"query":"mutation{\naddUser(firstName: \"Utsav\", lastName: \"Sharma\", username: \"utsav.sharma\")\n}","variables":{}}'

Now What if I wish to query all users using graphql and just want to get firstName instead of getting all the fields. Well you can simply do that using this command

curl --location --request POST 'http://localhost:4000/graphql' \--header 'Content-Type: application/json' \--data-raw '{"query":"{\n    users{\n        firstName\n    }\n}","variables":{}}'

There you have it, you have now successfully consumed APIs using GraphQL.

Conclusion

Now that you have seen how we can use GraphQL and the flexibility it provides, You can easily use that in your next project. However, keep in mind each of these architectures has its own pros and cons, most of the time which should one use over another is totally dependent on the use-case you have. Probably I will write up another post explaining which of these architectures you should go for and in what use cases.

References

· Source Code

· Postman Collection

· Apollo

· TypeORM