Anyone who’s working on, with, or has been reading about microservices has probably heard of gRPC in some form or another. In fact, anyone using a library from Google is probably already using it without even knowing.
gRPC is an open source project aimed to make service communication easy. Pinching the description from the gRPC website:
gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services. — https://grpc.io/about/
Let’s define a really simple service called Calculator, that will expose a single function named Add. It will take two numbers and simply return the result of adding these together:
1// This outlines which rpc functions our serivce2// names Calculator will expose. Including the input and3// output type definitions.45syntax = "proto3";67package calculator;89service Calculator {10 // Expose an RPC function called Add11 // that takes a message in the shape of AddResuest12 // and returns a message in the shape of AddReply13 rpc Add (AddRequest) returns (AddReply) {}14}1516// The AddRequest message shape, that had two properties17// that are both 32 bit integers.18message AddRequest {19 int32 number1 = 1;20 int32 number2 = 2;21}2223// The AddReply message shape, that had one property24// that is a 32 bit integer.25message AddReply {26 int32 result = 1;27}
Note: With gRPC we cannot have an input or output type that is a singular value. This is notable on the AddReply message. Normally we would just return the value directly rather than an object of result. The reason for this is extensibility, doing this allows us to add new data points without it being a breaking change.
Expose your service
Now that our service is defined we need to implement it using Node.js. As with most things in Javascript this starts off by installing a couple of node modules.
$ npm install grpc @grpc/proto-loader
Now we need to create our Node.js server, this is actually fairly simple. There is some simple bootstrapping of our gRPC server. But mainly we just need to implement the Add function:
1const path = require('path')2const PROTO_PATH = path.join(__dirname, 'calculator.proto')3const grpc = require('grpc')4const protoLoader = require('@grpc/proto-loader')56const packageDefinition = protoLoader.loadSync(PROTO_PATH, {7 keepCase: true,8 longs: String,9 enums: String,10 defaults: true,11 oneofs: true12})1314// Load in our service definition15const calculatorProto = grpc.loadPackageDefinition(packageDefinition).calculator1617// Implement the add function.18function Add(call, callback) {19 const { number1, number2 } = call.request // call.request will match the input from our .proto file20 const result = number1 + number22122 // The output on the callback should also match the output23 // in out .proto file24 callback(null, { result: result })25}2627const exposedFunctions = {28 Add: Add29}3031const server = new grpc.Server()32// Add our calculator service to our gRPC server.33server.addService(calculatorProto.Calculator.service, exposedFunctions)34server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure())35server.start();
Talk to your service
Now that we have our gRPC server running, we need to be able to call the functions that we have exposed. This is where our .proto file comes in to play again.
Each client that talks to your server needs to have a copy of your proto definitions. This allows the gRPC module to know what shape requests should take. There are a number of ways of sharing these but for now we can just copy and paste them around.
Creating a gRPC client is relatively simple and isn’t dissimilar from the code for the server:
1const path = require('path')2const PROTO_PATH = path.join(__dirname, 'calculator.proto')3var grpc = require('grpc')4var protoLoader = require('@grpc/proto-loader')56const packageDefinition = protoLoader.loadSync(PROTO_PATH, {7 keepCase: true,8 longs: String,9 enums: String,10 defaults: true,11 oneofs: true12})1314// Load in our service definition15const calculatorProto = grpc.loadPackageDefinition(packageDefinition).calculator1617const client = new calculatorProto.Calculator('localhost:50051', grpc.credentials.createInsecure())1819const params = {20 number1: 2,21 number2: 322}23client.Add(params, function(error, response) {24 if (error) console.log(error)2526 console.log('The Result Is: ' + response.result) // 'The Result Is: 5'27})
And Thats It
We now have a very simple gRPC client / server. This can be expanded significantly and there are lots of awesome guides on line and examples available here
