With all the madness currently going in the API world with GraphQL, you might be wondering how to migrate your existing REST APIs to GraphQL without breaking anything. This guide will help you accomplish your REST to GraphQL mission without changing your codebase so that you can REST with GraphQL! (pun intended 🤭)

REST to GraphQL

Well, GraphQL advocates have done a great job in marketing GraphQL already. Respecting their efforts, I won’t be going into details, but provide a summary:

  • GraphQL allows you to fetch multiple resources in a single request.
  • GraphQL solves the over fetching problem of REST by letting you describe your exact data needs.
  • GraphQL helps you solve the N+1 query problem on frontend by fetching related data in a single query.

What I will be touching upon in this guide instead is the aspect that most people miss while advocating GraphQL, which is, “We have already invested heavily in REST”. This means:

  • Most of our existing services are in REST.
  • We are more comfortable writing REST services.
  • We want to support existing clients using REST APIs.

While many articles help you migrate from REST to GraphQL, they all force you to change your existing codebase or write a new one in front of your REST services.

But wait…

If it works, then don’t touch it.

Wasn’t this the first rule of programming?

Migrations can be painful, especially the magnitude of a huge codebase can be daunting. There is always a chance of breaking something.

Why can’t we just REST?

Let’s face the fact - We all are lazy by nature. We love simple hacks and easy solutions.

What if there was a way wherein you could keep your REST services as is and still get a GraphQL layer on top of it without writing any code whatsoever? Sounds like a magic right? Well, Space Cloud helps make this possible.

What is Space Cloud?

To keep things simple,

Space Cloud is an open-source platform to build, secure and scale your apps. It provides instant GraphQL and REST APIs on your database and microservices.

The coolest part about Space Cloud is that all the APIs are realtime. You can optionally subscribe to changes in the database. This functionality comes in handy while making real-time apps.

It also has a deployments module that deploys and autoscales your services on Docker/Kubernetes.

However, in this guide, our focus will be on Space Cloud’s remote service module to migrate your REST services to GraphQL.

Like it already? Show your support ❤️ by giving us a star on Github.

The architecture

This is what the final architecture of GraphQL over REST looks like:

Rest services with Space Cloud

Your apps make a GraphQL query to Space Cloud, which in turn hits a REST endpoint on your server. Space Cloud acts as a GraphQL proxy or API gateway in this case.

As you would have noticed, Space Cloud is a separate GraphQL layer that sits on top of your REST services. The beauty of this fact is that your REST services are still intact, and you can use them directly in your existing clients. This fact lets you ship your REST service to GraphQL without breaking your older clients.

Great! We have understood what Space Cloud is and how it sits with our application. Let’s get straight to some action!

What we will be doing

In this guide, we will be building a simple REST service which has the following endpoints:

  • Adder endpoint: POST /adder
  • Doubler endpoint: GET /doubler/:num

The adder endpoint will return the sum of two numbers that it receives in request body. The doubler endpoint, on the other hand, will return double of the number that it receives in URL path parameter.

First we will access the above endpoints via REST. Thereafter, we will simply use Space Cloud’s remote services module to access these endpoints via GraphQL.

Great! Let’s start building things now!

Note: Don’t worry if you get stuck somewhere. You can always ping me on this Discord server, and I will help you out personally.

Step 1: Writing a service

Note: Even if you are reading this from your mobile right now, you can still follow this guide along just by reading.

Let’s start by writing our REST service. We are going to write our REST service in NodeJS using Express.

Note: You can write your service in any language or framework as long as it can speak HTTP since that’s the protocol Space Cloud is going to use to communicate with your REST service.

First, create a folder that serves as our working directory.

Create NPM project:

npm init -y

Install Express:

npm install --save express

Write express server

Create a file index.js and copy paste the following code:

var express = require("express");
var app = express();

app.use(express.json());

app.post("/adder", function(req, res) {
  const num1 = req.body.num1;
  const num2 = req.body.num2;
  const response = { value: num1 + num2 };
  res.status(200).send(JSON.stringify(response));
});

app.get("/doubler/:num", function(req, res) {
  const num = Number(req.params.num);
  const response = { value: num * 2 };
  res.status(200).send(JSON.stringify(response));
});

var server = app.listen(8080, function () {
    console.log("app running on port:", server.address().port);
});

As you can see, the code is pretty straight forward. We have just created an HTTP server using ExpressJS that is listening on port 8080.

The server has two endpoints, as we discussed earlier:

  • Adder endpoint: We are expecting to receive two numbers - num1 and num2 in the request body. All we are doing is returning the sum of these two numbers.
  • Doubler endpoint: We are simply returning the double of the number that we are receiving in the URL path parameter.

That’s all we need to write for this service.

Note: For migrating your existing REST services, you don’t need to write/modify any code. And if your service is already running, you can even skip the 4th step.

Step 2: Install Space Cloud

We are using space-cli to run Space Cloud for us.

Download space-cli

space-cli is the cli tool for Space Cloud. You can download a version for your particular platform:

Unzip the compressed archive.

For Linux / Mac: unzip space-cli.zip && chmod +x space-cli

For Windows: Right-click on the archive and select extract here.

Copy the space-cli binary to your environment path variable for global usage.

For Linux / Mac: Copy the space-cli to /usr/local/bin. You may have to use sudo depending on the permissions of your /usr/local/bin.

For Windows: Add the path of the space-cli.exe to the environment variable PATH for making space-cli accessible globally.

To make sure if the space-cli binary is installed correctly, type the following command:

For Linux / Mac: space-cli --version

For Windows: space-cli.exe --version

This prints the space-cli version.

Run Space Cloud

We can set up all Space Cloud components (gateway and runner) via space-cli using a single command.

space-cli setup --dev

The setup command selects Docker as a target by default and runs all the containers required to setup Space Cloud. On successful installation it generates an output similar to this one:

INFO[0000] Setting up Space Cloud on docker.            
INFO[0000] Fetching latest Space Cloud Version         
INFO[0000] Starting container space-cloud-gateway...    
INFO[0000] Image spaceuptech/gateway:latest already exists. No need to pull it again 
INFO[0000] Starting container space-cloud-runner...     
INFO[0000] Image spaceuptech/runner:latest already exists. No need to pull it again 

INFO[0001] Space Cloud (id: "local-admin") has been successfully setup! 👍 
INFO[0001] You can visit mission control at http://localhost:4122/mission-control 💻 
INFO[0001] Your login credentials: [username: "local-admin"; key: "KkYr6FvgYsvr"] 🤫

Verify Space Cloud installation

Verify the installation by running the following docker command:

docker ps

You should see an output similar to this:

CONTAINER ID        IMAGE                        COMMAND             CREATED              STATUS              PORTS                                            NAMES
507ce4042486        spaceuptech/runner:latest    "./app start"       About a minute ago   Up About a minute                                                    space-cloud-runner
33a5a7a9be3a        spaceuptech/gateway:latest   "./app run"         About a minute ago   Up About a minute   0.0.0.0:4122->4122/tcp, 0.0.0.0:4126->4126/tcp   space-cloud-gateway

Step 3: Configure Space Cloud

If you exec into docker container of Space Cloud’s gateway, you can see a config.yaml file generated by gateway in the /app directory.

Space Cloud needs this config file to function. The config file is used to load various information like which databases to use, their connection strings and security rules.

Space Cloud has it’s own Mission Control (admin UI) to configure all of this quickly.

Open Mission Control:

Head over to http://localhost:4122/mission-control to open Mission Control.

Note: Replace localhost with the address of your Space Cloud if you are not running it locally.

Create a project:

Click on Create a Project button to open the following screen:

Name your project as MyProject.

Hit Create Project button to create the project.

After creating the project, the next step would be adding a database to your project:

However, we can simply skip this step as we don’t need a database for this project.

After that you would be directed to the overview page of your project:

That’s all we need to configure as of now.

Step 4: Deploying the REST service

You can skip this step if you already have a REST service deployed.

Space Cloud can deploy only docker containers as of now. So we need to dockerize our app. We are going to take the help of space-cli to do that.

First of all, we need a docker registry that can host the docker images of our service. Run this command to spin up a docker registry locally:

space-cli --project myproject add registry

In production, it is recommended to use a managed container registry

Now we need yo generate two files:

  • Dockerfile - To build the docker image.
  • service.yaml - The service configuration (example: resources, auto-scaling, ports) to deploy this service via Space Cloud.

space-cli has a built-in command to generate both of these automatically for us. Just run the following command:

space-cli deploy --prepare

It is going to ask you a bunch of questions. Answer them with the following required values and leave the rest to default:

Project Id Service Id
myproject myapp

Great! We now have a Dockerfile and a service.yaml. Feel free to explore and change both these files before finally deploying the service. The service.yaml file looks something like this:

api: /v1/runner/{project}/services/{id}/{version}
type: service
meta:
  id: myapp
  project: myproject
  version: v1
spec:
  scale:
    replicas: 1
    minReplicas: 1
    maxReplicas: 100
    concurrency: 50
    mode: parallel
  labels: {}
  tasks:
  - id: myapp
    ports:
    - name: http
      protocol: http
      port: 8080
    resources:
      cpu: 250
      memory: 512
    docker:
      image: localhost:5000/myproject-myapp:v1
      cmd: []
      secret: ""
      imagePullPolicy: "pull-if-not-exists"
    env: {}
    secrets: []
    runtime: image
  affinity: []
  whitelists:
  - projectId: myproject
    service: '*'
  upstreams:
  - projectId: myproject
    service: '*'

The only step left now is building the docker image and deploying it via Space Cloud.

We are going to use the deploy command of space-cli for that. It first builds a docker image for us using the Dockerfile (generated in the above step) and then publishes it to the docker registry. Once it’s done publishing, it uses the config in service.yaml file to deploy the service via Space Cloud.

Enough of talking. Let’s hit the magical command now:

space-cli deploy

You may have to run the above command with sudo privileges if your docker is not in the sudoer group.

Verify the deployment

Checkout to the Overview tab of Deployments section in Mission Control and hit refresh. You should be able to see the service we just deployed like this:

Deployments

Expose your service

Currently, the REST service we deployed is accessible from within the cluster only. We need to add Space Cloud Routes to expose our service to the outside world.

Let’s head over to the Ingress Routing section in the Microservices tab in Mission Control.

Hit Create your first route to open the following form:

Routing Form

We’ll simply redirect all non Space Cloud traffic to our service for now.

You’ll need to provide the following details in the form:

Route Matching Type Prefix
Prefix Match /

Targets:

Scheme Service Host Port Weight
HTTP myapp.myproject.svc.cluster.local 8080 100

Once you are done, hit Add.

To verify that our REST service is exposed, simply open another tab in your browser and enter:

http://localhost:4122/doubler/3

You should be able to see the following response on your screen:

{
  "value": 6
}

Great! We have our REST service up and running. The REST endpoints can be consumed by outside world as well. Time to expose these REST endpoints to GraphQL. (Without writing any extra code whatsoever 😛).

Step 5: Adding GraphQL to your REST endpoints

Head over to the GraphQL section under Microservices in Mission Control.

Click on the Add first remote service button.

In the form, enter the following details:

Service Name URL
myapp http://myapp.myproject.svc.cluster.local:8080

Note: myapp.myproject.svc.cluster.local is the domain created by space cloud for internal use. The domain format looks like this <service_id>.<project_id>.svc.cluster.local. If you have an existing REST service, provide it’s host name here.

Hit Add. This will add a GraphQL API over the service.

Unfortunately, Space Cloud cannot automatically discover our APIs. This is because of the way HTTP works in general. There is no introspection feature available for Space Cloud to follow the APIs.

To overcome this, we need to add endpoints.

Click the View action against our newly added myapp service.

This is where you add endpoints. We have two endpoints. Let’s go and add them now!

Hit the Add your first endpoint button.

Fill up the form as shown below:

Endpoint name Endpoint type Method Path
adder Internal POST /adder

Now let’s create the next endpoint for doubling the result. Enter the following in the form:

Endpoint name Endpoint type Method Path
doubler Internal GET /doubler/{args.num}

Notice the {args.num} in the path? This is actually referring to the parameters being passed to the GraphQL query. Don’t worry much about it for now. It will get clearer as we move forward.

Cool! That’s about it! All that left’s to do is playing around with the API we just created.

Step 6: Query your REST service via GraphQL

We have added our REST service and the two endpoints as well to Space Cloud. Time to query it using our unified GraphQL API.

Head to the Explorer section:

Explorer

Try running the following GraphQL query in the GraphiQL explorer:

query {
  adder(num1: 10, num2: 20) @myapp {
    value
  }
}

The above GraphQL query is translated by SC to a REST call as follow:

  • Method: POST
  • Endpoint: /adder
  • Request body:
{
  "num1": 10,
  "num2": 20
}

On successful invocation, you should be able to see a response like this:

{
  "data": {
    "adder": {
      "value": 30
    }
  }
}

Hurray! We just migrated our Rest API to GraphQL in minutes.

Now let’s understand what’s going on here.

The service name (myapp in this case) gets reflected as a directive (@myapp) in our GraphQL query. The endpoint names (adder and doubler) becomes field names.

The arguments that we passed to the adder field in the GraphQL query becomes available in the request body to the adder endpoint.

Let’s try querying the doubler endpoint with the following GraphQL query:

{
  doubler(num: 50) @myapp {
    value
  }
}

You will get a response like this:

{
  "data": {
    "doubler": {
      "value": 100
    }
  }
}

The above GraphQL query is translated by SC to a REST call as follow:

  • Method: GET
  • Endpoint: /doubler/50

Remember the url we had entered for the doubler endpoint? It looked like this: /doubler/{args.num}.

The {args.num} would probably make more sense now. It was instructing Space Cloud to Generate the URL using the arguments provided in the GraphQL query. This helps you generate dynamic URLs.

Bonus - Service Chaining

Having followed this guide till here successfully, we all deserve a well-earned bonus! Let’s see how this REST to GraphQL transition unlocks a super awesome power, Service Chaining, for us.

Let’s take a scenario where:

  • We want to sum two numbers using the adder service.
  • Double the result we got from the adder service.

The REST way

If we were using REST in our client-side code, the above task would have looked like this:

REST way

Notice, how we are making two requests from the frontend which means twice the roundtrip time. It leads to a slow response time and poor user experience.

The GraphQL way

Now if we switched the client from REST to GraphQL using Space Cloud, our requests will look like this:

GraphQL way

Notice, here we are making only one GraphQL query from the frontend to the backend (Space Cloud). Space Cloud, in turn, is making two requests to your REST server to fulfil this request. However, the round trips of these requests (from Space Cloud to your server) are negligible as they are in the same network.

The GraphQL query to Space Cloud to achieve the above task will be:

{
  adder(num1: 10, num2: 20) @myapp {
    doubler(num: "adder.value") @myapp {
      value
    }
  }
}

Notice, how we are calling the doubler service after the response of adder service and passing the result of adder service to the doubler as an argument.

The response for this query will look like this:

{
  "adder": {
    "doubler": {
      "value": 60
    }
  }
}

As you would have guessed we got 60 as a result ((10 + 20) * 2).

Additional tip: If you wanted to query two unrelated REST services parallelly, you could have done that too in a single request like this:

{
  adder(num1: 10, num2: 20) @myapp {
    value
  }
  doubler(num: 50) @myapp {
    value
  }
}

I will leave the response part of this query to you as an assignment😛.

Conclusion

First of all, give yourself a pat on the back for having followed this guide to the end.

We learned that:

  • Migrating from REST to GraphQL doesn’t require code changes.
  • We don’t need to choose between REST and GraphQL. We can support both REST and GraphQL together in the same application.
  • Using GraphQL with Space Cloud provides us with some neat networking benefits and helps you reduce your round trips.

Well, there’s a lot more you can do with Space Cloud other than migrating from REST to GraphQL (eg: cross-database joins). Give Space Cloud a star on Github if you like it ❤️. Join our Discord server if you wanna bounce off some ideas or have any questions.