How to Build a GraphQL Data Layer for REST Microservices

Written by gethackteam | Published 2022/01/07
Tech Story Tags: graphql | microservices | rest | rest-api | programming | coding | software-development | how-to

TLDRGraphQL is a great technology for microservices because you can use it as a data layer for these microservices. One use case is to combine the data from all these services into a single, unified API. But building this data layer can be very time-consuming since you need to connect the microservices into one schema. In this post, you'll learn step-by-step how to build a data layer for microservices with GraphQL in a declarative way.via the TL;DR App

GraphQL is a great technology for microservices because you can use it as a data layer for these microservices.

One use case is to combine the data from all these services into a single, unified API.

But building this data layer can be very time-consuming since you need to connect the microservices into one schema. In this post, you'll learn step-by-step how to build a data layer for microservices with GraphQL in a declarative way.

TL;DR: The complete source code for this post can be found here.

Why use GraphQL for Microservices?

Whenever you have a microservices architecture, you often don’t want to expose this architecture to the (front-end) clients that consume it.

But you use a data layer (or gateway) that serves as the entry point to your microservices.

This way you don’t give any critical information about your microservice orchestration away, which makes it less prone to hackers.

By having one single entry to your microservices, you reduce the number of requests from your clients to these services. Calls to-- for example-- your authentication service to verify a token before every request, don’t have to be repeated from the client.

Also, the data layer saves you (and others consuming your APIs) time documenting and specifying all the APIs from the microservices.

The microservices that are consumed through this data layer don’t necessarily have to use GraphQL themselves. By using a tool called StepZen, you can create a data layer for REST microservices, without having to write any code.

Instead, you can connect the microservices using GraphQL SDL (Schema Design Language) only. For every microservice, a GraphQL schema must be created, which can then be combined together into one GraphQL schema with StepZen. Let’s see how this would work.

Building a Data Layer Using GraphQL

Now you’ve learned why a data layer is helpful for your microservices, it’s time to learn how you can build such a data layer.

There are many ways to create a data layer by creating a custom GraphQL server, but this is very time-consuming and error-prone. Luckily, the tool StepZen makes it easier to create a GraphQL data layer (or server) by writing GraphQL SDL only.

By using this declarative approach you don’t only save time writing code, but it also means you don’t have to handle the deployment of the data layer yourself. StepZen deploys the data layer for you automatically on every save.

Let’s create the data layer, based on three (mocked) REST microservices that you can find in this Github repository.

Setting up the Project

The README of the project has all the information on running the mock REST microservices, by using npm or yarn.

First, you need to clone the repository to your local machine and install the JavaScript dependencies. After which you can run either npm start or yarn start.

The REST microservices can then be found on localhost ports 3001 to 3003, but also through an HTTPS tunnel. StepZen needs your microservices to be available though HTTPS, and therefore I use the tool Localtunnel. You don’t have to install this tool separately, as it was already installed from the projects’ dependencies.

After starting the project, something like this will be logged to your terminal:

Posts service is running on https://lorem-mouse-38.loca.lt
Users service is running on https://smart-robin-23.loca.lt
Auth service is running on https://mouse-trap-12.loca.lt

What is logged to terminal are the hostnames for the HTTPS tunnels to the mocked REST microservices.

The Posts service in example can be visited in the browser at http://localhost:3003/api/posts, but also via the HTTPS tunnel at https://lorem-mouse-38.loca.lt/api/posts.

You need to add these hostnames to the file config.yaml for every service, so StepZen can use them to create the data layer:

## config.yaml

configurationset:
  - configuration:
      name: auth_service
      client_id: test
      client_secret: test123
      hostname: 'mouse-trap-12.loca.lt'
  - configuration:
      name: posts_service
      hostname: 'lorem-mouse-38.loca.lt'
  - configuration:
      name: users_service
      hostname: 'smart-robin-23.loca.lt'

We continue by create a GraphQL schema for the services that are now connected through the HTTPS tunnel.

Creating Schemas for Microservices

First, we’ll create a GraphQL schema for the authentication service.

This service is available on the endpoint /api/token, and the hostname that was generated by Localtunnel - which is different every time you start the microservices. From this endpoint you can create an authorization token using OAuth.

The OAuth flow for client credentials is applied, which lets you create a token by calling https://$hostname/api/token?grant_type=client_credentials&client_id=$client_id&client_secret=$client_secret. The authentication service will return a Bearer token if the values for $client_id and $client_secret are correct. The GraphQL schema to reflect the authentication service can be found in services/auth/index.graphql:

## /services/auth/index.graphql

type Auth {
  id: Int!
  access_token: String!
}

type Query {
  token: Auth
    @rest(
      endpoint: "https://$hostname/api/token?grant_type=client_credentials&client_id=$client_id&client_secret=$client_secret"
      configuration: "auth_service"
    )
}

The query called token is using the custom directive @rest, which can be used to define a REST data source that will be called by StepZen. From the file config.yaml, the values for $client_id and $client_secret will be retrieved.

This query from the authentication service is how the other microservices can get the authorization token needed in the OAuth flow.

For the microservice that gets posts, it means it should have the token before it’s able to retrieve the posts. The schema for the posts service is in services/posts/index.graphql:

## /services/posts/index.graphql

type Post {
  id: Int!
  title: String!
  imageUrl: String!
  author: Int
}

type Query {
  posts(access_token: String!): [Post]
    @rest(
      endpoint: "https://$hostname/api/posts"
      headers: [{ name: "Authorization", value: "Bearer $access_token" }]
      configuration: "posts_service"
    )

  getPosts: [Post] @sequence(steps: [{ query: "token" }, { query: "posts" }])
}

The schema for the posts service has two queries: one to retrieve the posts, and one to retrieve the token and then get the posts. A custom directive called @sequence is used to combine the posts query from this GraphQL schema with the token query from the authentication service.

The response of the token query is used as input for the posts query, which allows you to return the posts when the token can be retrieved.

You can reuse this setup for the users service, that also has a query to get the users with the token. The schema for this REST microservice can be found in /services/users/index.graphql:

## /services/users/index.graphql

type User {
  id: Int!
  name: String!
}

type Query {
  user(id: Int!, access_token: String!): User
    @rest(
      endpoint: "https://$hostname/api/users/$id/"
      headers: [{ name: "Authorization", value: "Bearer $access_token" }]
      configuration: "users_service"
    )

  getUser(id: Int!): User
    @sequence(steps: [{ query: "token" }, { query: "user" }])
}

The GraphQL schemas for the three REST microservices in this project need be combined into one GraphQL schema. This happens in index.graphql:

## index.graphql

schema @sdl(files: ["services/auth/index.graphql", "services/posts/index.graphql", "services/users/index.graphql"]) {
  query: Query
}

Finally, in the file stepzen.config.json the endpoint to which the GraphQL data layer will be deployed is defined. In this project it’s set to /api/datalayer, meaning when you run the command to start (and deploy) the GraphQL server the GraphiQL playground becomes locally available at http://localhost:5000/api/datalayer.

You can run stepzen start to combine the GraphQL schemas from all the REST microservices, which will then be deployed to StepZen:

After running this command you can start developing applications that consume this GraphQL data layer instead of the microservices directly.

Summary

Great work! You’ve learned how to create a GraphQL data layer for REST microservices, using GraphQL SDL only.

This is done with the tool StepZen, which has a generous free tier. With StepZen you cannot only create a data layer for REST microservices, but you can also create individual GraphQL APIs. You can learn more about this by looking through StepZen documentation.

Previously published here.


Written by gethackteam | Tech Author, Speaker and Entrepreneur
Published by HackerNoon on 2022/01/07