When GraphQL meets gRPC… 😍
Tags: graphql, kubernetes, grpc
Published: 2020-02-11 11:16:39

*This article was originally written on [Medium](https://medium.com/@svengau/when-graphql-meets-grpc-3e9729d32e05)*

Context
=======

At [Aiptrade](http://www.aiptrade.com), we have a micro-service architecture, deployed on Kubernetes:

- backend services discuss with each others via pub-sub,
- and ONLY one frontend service to manage the API. Argh.

This frontend service has grown and is now fat, so it’s time to truncate it, and move to a **Backend for Frontend** (BFF) architecture.  
At first, we tried [**GraphQL schema stitching**](https://www.apollographql.com/docs/graphql-tools/schema-stitching.html), which sounds really awesome, but it comes with some problems: conflict management between types of the same name, heterogeneous error handling, and we were also worried about performance, since a graphQL request is parsed twice.  
So we turned to gRPC. Very promising on paper, library available in several languages, with a typed contract similar to GraphQL, used for several years by Google internally, in short the Holy Grail! 🤩

I won’t explain GraphQL [gRPC](https://grpc.io) nor [protobuffer](https://developers.google.com/protocol-buffers/), there are plenty of articles and the official documentation is good. So let’s dig into the project.

The sample project
==================

Our PoC will allow us to create… well… blog posts as usual 😄, and is composed with:

- a **graphQL server**, in front of the client as our unique BFF entry point. Its role is mainly to validate input data (required / optional fields, type, …) and filter output data. The validation here is important since required & optional fields have been dropped from proto3. It also acts as a client to the gRPC micro-service.
- a **gRPC server**, to perform all functional operations. Here to create and list some blog posts.
- those 2 micro-services will be hosted on k8, so we will also implement a **Health check** mechanism, in gRPC please!

![Sample Architecture diagram with GraphQL & gRPC](https://miro.medium.com/max/1840/1*o9_bjlKXlMjii3G7BCNb-Q.png)

Sample Architecture diagram with GraphQL & gRPC (cc cloudcraft.co)

Setup
=====

For the GraphQL Server, [apollo-server](https://www.npmjs.com/package/apollo-server) does the job perfectly. And it offers a very convenient graphQL playground to run our queries.

For gRPC, We will use **NodeJS** for the server and client side, because gRPC libraries on NodeJS **can dynamically generate the code at runtime**, which is awesome to quickly do a PoC, unlike other libraries available on other platforms (Go, PHP, Java, Python,…), that require the [protoc](https://github.com/protocolbuffers/protobuf) compiler to generate a stub at build time. If you prefer working with static generated code, [here](/attest-engineering/fully-typed-typescript-api-consuming-grpc-and-graphql-5d5ae6b33bf1) a really interesting article.

- On the **client side**, the official [gRPC](https://www.npmjs.com/package/grpc) client works like a charm. It can handle interceptors, but doesn’t support Promises. If you prefer working with async / await, [grpc-caller](https://www.npmjs.com/package/grpc-caller) is a good alternative.
- On the **server side**, The official [gRPC-node](https://github.com/grpc/grpc-node) library doesn’t support interceptors, so we have chosen [mali](https://github.com/malijs/mali), a really simple and easy to use library for gRPC, which supports metadata and middlewares (aka interceptors).

To run the PoC:

```
git clone [git@github.com](mailto:git@github.com):svengau/grpc-graphql-sample.gitcd post-api && npm i && npm start  
cd graphql-api && npm i && npm start🚀 Server ready at [http://localhost:4000/graphql](http://localhost:4000/graphql)
```

and run the following query:

```
mutation {  
 addPost(data: { title: “helloooo” }) {  
   message  
   result { \_id title body }  
 }  
}
```

The wedding contract
====================

The gRPC server exposes 2 proto3 contracts:

- **Post.proto**, a sample contract to create and list blog posts.
- **Health.proto**, a [contract proposed](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) by gRPC to check service Health from Kubernetes.

The proto3 contract, in the `post.proto` file:

```
message Post {  
  string \_id = 1;  
  string title = 2;  
  string body = 3;  
}message Posts {  
  int32 page = 1;  
  int32 limit = 2;  
  int32 count = 3;  
  repeated Post nodes = 4;  
}message addPostRequest {  
  reserved 1; // \_id  
  required string title = 2;  
  string body = 3;  
}message listPostRequest {  
  optional string page = 1 \[default = 1\];  
  optional string limit = 2;  
  optional string \_id = 3;  
}service PostService {  
  rpc addPost (addPostRequest) returns (Post) {}  
  rpc listPosts (listPostRequest) returns (Posts) {}  
}
```

And to serve the proto file, as simple as:

```
import Mali from "mali";this.server = new Mali();this.server.addService("\[...\]/Post.proto", "PostService");  
this.server.use({ PostService: { addPost, listPost } })this.server.start("0.0.0.0:50051");
```

Security
========

gRPC supports natively [2 mechanisms](https://grpc.io/docs/guides/auth.html):

- **SSL/TLS**: this will encrypt all the data exchanged between the client and the server, and works at the channel level.
- **Token-based authentication with Google**: aka OAuth2 tokens, and must be used on an encrypted channel.

SSL/TLS is a must have since Google doesn’t allow any unencrypted connections with its services. We won’t use token-based authentication, but a simple mechanism based on an API key to illustrate how work interceptors. By the way, [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/grpc/about-grpc) uses also a similar mechanism to restrict access to their API.

SSL
---

To generate SSL certificates for CA, client and server, just launch in post-api:

```
src/cert/generate\_using\_openssl.sh
```

I’ve also put another sample script which uses [certrap](https://github.com/square/certstrap), a convenient tool provided by Foursquare.

Once generated, the certificates use is [well-documented](https://grpc.io/docs/guides/auth.html#nodejs) in the official gRPC site, basically:

```
const credentials = grpc.ServerCredentials.createSsl(  
 fs.readFileSync(\_\_dirname + ‘/cert/ca.crt’),  
 \[{  
   cert\_chain: fs.readFileSync(\_\_dirname + ‘/cert/server.crt’),  
   private\_key: fs.readFileSync(\_\_dirname + ‘/cert/server.key’)  
 }\], true);this.server.start("0.0.0.0:50051", credentials);
```

And on the client side:

```
const packageDefinition = protoLoader.loadSync(... + '/Post.proto');const  proto = grpc.loadPackageDefinition(packageDefinition);let credentials = grpc.credentials.createSsl(  
 fs.readFileSync(\_\_dirname + ‘/../cert/ca.crt’),  
 fs.readFileSync(\_\_dirname + ‘/../cert/client.key’),  
 fs.readFileSync(\_\_dirname + ‘/../cert/client.crt’)  
 );const options = {  
 "grpc.ssl\_target\_name\_override": "localhost",  
};const client = proto.sample.PostService(host, credentials, options);
```

️**Small tip 💡:** the certificates have been generated to use with localhost, so the option `grpc.ssl_target_name_override` allows us to reuse the same certificates with a remote gRPC server. Without this option, you have to generate new certificate with the right domain name.

Sample authorization mechanism using interceptors
-------------------------------------------------

Mali offers a great mechanism to intercept calls done to the API. You can intercept globally, at the service level, or at the operation level. In our case, Health service stays insecure, and we secure the PostService like this:

```
function **auth(apiKey: string)** {  
 return async function(ctx: any, next: any) {  
   const apiKeyProvided:string = ctx.request.get("x-api-key");  
   if (!apiKeyProvided || apiKeyProvided !== apiKey) {  
     throw new Error(‘invalid.apiKey’);  
   }  
   await next();  
  }  
}this.server = new Mali()  
this.server.addService("\[...\]/Post.proto", "PostService");  
this.server.use("PostService", **auth("myapikey")**);  
this.server.use({ PostService: { addPost, listPost } });
```

On the client side, you can pass the API key, through metadata, globally:

```
import \* as grpc from 'grpc';const interceptorAuth:any = (options:any, nextCall:any) =>  
   new grpc.InterceptingCall(nextCall(options), {  
     start: function(metadata, listener, next) {  
       metadata.add(‘x-api-key’, API\_KEY);  
       next(metadata, listener);  
     }  
   });const client = new proto.sample.PostService(  
  host, credentials, {interceptors: \[interceptorAuth\]}  
);
```

Or you can pass metadata at the operation level:

```
import \* as grpc from 'grpc';const metadata = new grpc.Metadata();  
metadata.add(‘x-api-key’, API\_KEY);client.listPosts({page: 1}, metadata, (err:any, response:any) => {  
 console.log(‘Post list’, err, response);  
 });
```

Error handling
==============

With grpc-node, you have to catch all exceptions, which could quickly become a pain …. Fortunately, Mali catches exceptions for you and sends back to the graphQL server as an error.

![Error thrown by gRPC and send back using GraphQL API](https://miro.medium.com/max/4040/1*uK2J3SWbnZbF7wH2Va8drg.png)

Error thrown by gRPC and send back using GraphQL API

Deployment on K8
================

Kubernetes doesn’t support gRPC health checks natively, but gRPC comes with:

- a sample [proto contract](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) to implement, with a **Check** method.
- the tool [grpc-health-probe](https://github.com/grpc-ecosystem/grpc-health-probe/) to install in your docker image, which will call the **Check** method.

[grpc-health-probe](https://github.com/grpc-ecosystem/grpc-health-probe/) needs to be configured to run with SSL and the command line is a bit long:

```
/bin/grpc\_health\_probe   
 -tls   
 -tls-server-name localhost   
 -tls-ca-cert cert/ca.crt   
 -tls-client-cert cert/client.crt   
 -tls-client-key cert/client.key   
 -addr=:4000
```

So I’ve put it in a script called`bin/grpc-health-probe.sh`

Once the Check service up and running, you just need to configure k8 with:

```
spec:  
 containers:  
 - name: server  
  image: "\[YOUR-DOCKER-IMAGE\]"  
  ports:  
   - containerPort: 4000  
   readinessProbe:  
    exec:  
     command: \["/usr/src/app/src/bin/grpc\_health\_probe.sh", ":4000"\]  
     initialDelaySeconds: 5  
   livenessProbe:  
    exec:  
     command: \["/usr/src/app/src/bin/grpc\_health\_probe.sh", ":4000"\]  
     initialDelaySeconds: 10
```

Conclusion
==========

gRPC and GraphQL work great together:

- GraphQL is in charge of setting up a contract with the outside world,
- while gRPC is in charge of communication between micro-services within the company.

Not all gRPC libraries are at the same level, both in terms of documentation and code. Better reading the doc carefully (proto version, authentication, interceptor support) before starting with a library.  
For Nodejs, server-side interceptors are sorely lacking in the official library.

Fortunately, Mali offers a very good alternative.

Retrieve the project on Github:


{% github svengau/grpc-graphql-sample %}


And Happy coding ! 😎

References
==========

- https://grpc.io/docs/quickstart/node.html
- https://developers.google.com/protocol-buffers/docs/overview
- https://github.com/grpc-ecosystem/awesome-grpc
- https://opensource.googleblog.com/2015/02/introducing-grpc-new-open-source-http2.html