Given GraphQL is quite a fresher in the realm of software development, a lot of people are not yet confident whether to write the next API in GraphQL or not. Obviously, many of us have been comfortable with REST APIs and have deployed in production with confidence.
In GraphQL there are a lot of open questions like versioning, authentication/authorization, caching, pagination, etc that needs to be answered in detail.
In this series of GraphQL blog posts, I will pick one topic and we will dive deep together.
Tip: optimize your coding by building, sharing and consuming reusable JS components with Bit (Github). Turn modular components into shared-building blocks to help your team build better applications faster.
The goal of this post
This blog post will help you understand what parts of a REST API need to be modified when you want to migrate an existing REST API to GraphQL API. We will also see which components can be combined together to make up a GraphQL API.
We have an existing set of REST APIs written with express.js in Node.js and by the end of this post, we will migrate them to GraphQL APIs.
The following is the URL of the same repo.
I have also created a pull request to show what files exactly changes when you migrate from REST to GraphQL, if you are curious right away, check the pull request in above repository.
If you want to know the basics of what is a GrahpQL, refer to my last blog post.
Let’s continue on understanding the fundamentals and efforts needed to migrate.
Existing REST APIs to migrate
Let’s first quickly look at our existing REST APIs and the structure of how the backend service is written. So that we are on the same page and together we can decide which part of codebase to modify.
This Node.js app exposes a list of APIs where you can do the following:
GET: /api/employeesreturns a list of all employees
GET: /api/employess/:idreturns the details of an employee with id passed as a param
POST: /api/employeescreates a new employee
DELETE: /api/employees/:iddeletes the employee who has the id passed as a param
This list of APIs will cover all different types of APIs that are developed in most of the apps. The technical layer is going to be the same, only business or use case layer will differ.
The following is the code snippet from our existing node app where these APIs are constructed.
If you want to run the server and check the implementation refer to this repository.
The above code snippet is the controller layer which is the mapping between the API routes and the various services.
GET of /api/employees/:id will call the function
employeeService.getById(employeeId) from employeeService.
The APIs in this Node.js app is following the very common design of service, controller and repository layers.
- Controller layer: responsible for acting as a bridge between client-side and the service layer.
- Service layer: responsible for holding various business logic.
- Repository layer: responsible for interacting with data source or database and passing the data to the service layer.
Let’s see how this is going to help in maintaining the app in future, suppose if we want to change the database from Mysql to Postgres, then only the code in the repository layer needs to be changed, the controller and service layer will remain untouched. This abstraction helps in defining the boundaries and efficiently distributes the responsibilities.
This design will also help us in migrating from REST to GraphQL. We need to only make changes in the controller layer and rest everything remains the same.
GraphQL controller layer
The GraphQL is the thinnest layer in the application, which acts as an interface between the client and the service layer.
In REST APIs we have one URL for specific API, which ends up with hundreds of different API endpoints, whereas in GraphQL we expose only one endpoint.
Hence, the GraphQL controller has only one URL i.e.
POST /graphql and the all the query is passed to this endpoint as the request body.
Existence of GraphQL and REST APIs together
As GraphQL is just another POST endpoint, the REST APIs and GraphQL APIs can live together in one API. This will also help if you want to slowly migrate to GraphQL without removing all the REST APIs.
The following image can be used to visualize the existence of GraphQL in any application.
Steps to migrate REST API to GraphQL
Let’s open the code and start migrating our REST APIs to GraphQL APIs.
Step 1 for migration:
Install GraphQL dependencies
npm install express express-graphql graphql --save
Step 2 for migration:
Create a new GraphQL endpoint in express
Step 3 for migration:
Create a controller file and build Schemas
In the above piece of code,
type Query holds all the get APIs and
type Mutation holds all the creation, update or delete APIs.
line 9: As the GraphQL schema is strictly typed for all the input and return type we need to mention the types, e.g
Int type, if you enter a string it will throw an error. The
! operator at the end of type signifies that the input is mandatory.
type Query for getting queries and
type Mutation for anything to mutate the values.
The Schema in line 7 is the basic, most important and the building block of GraphQL. This also helps in setup up the documentation of APIs for the client who wants to query. Let’s see how this schema creates the documentation of our GraphQL APIs:
The above is the screenshot of the GraphQL provides the query interface out of the box, this can be referred to as the documentation of the APIs. On left most section you write your query which is equivalent to POST request body if you try in Postman or other query tool and the small menu is an autocomplete list of fields that can be queried in employees query.
The rightmost section is a clickable explorer of different queries and mutation APIs which lists the various inputs and their return type
The middle section is the response of the query.
Step 4 for migration:
Bind the controller and services
Continuing after we wrote our schema, now we need to bind the
Mutation resolver with our existing services.
In the following code snippet, line 9 and line 31 actually binds the schema with the service to called, the key
employee matches and calls the function defined in rootResolver(line 31) i.e.
employeeService.getById, in REST implementation this function was defined and bound to the
router.get, refer below.
Similarly, for Mutation line 13 and line 33 have the same key, and hence the function
employeeService.save is called.
In line 40, if graphiql is set to true this creates an amazing interactive window for checking the documentation and playing around with GraphQL APIs.
Note: In case you are not following controller-service separation design, and the implementation is tightly coupled with the API endpoint in the controller, you can take out the code in a function, and this function can act as a pseudo service or you can actually create a service layer.
Step 5 for migration:
Setup graphqlHttp, schemas, and resolvers
The last step is to use the graphqlHTTP and set the schema and definition of which key in schema call which function or service.
In the above code, snippet check line 37.
If you want to quickly play around with GraphQL, play around the following code sandbox. Start with Ctrl + space to get autocomplete menu by query or mutation. Read the guide in the sandbox to get more details about how to write the GraphQL query.