How to Create an API Using gRPC and Node.js Posted in Design Vyom Srivastava August 2, 2022 gRPC is a modern open-source RPC framework that can run in any environment. It is used by many large companies, including Google, to power some of their most popular services. gRPC is also an excellent fit for Node.js applications due to its high performance and small footprint. This guide will show you how to get started with gRPC in Node.js with a simple example. We’ll create a straightforward service that allows us to get information about a user. To do this, we’ll first need to define our service in a .proto file. Then, we’ll generate the necessary Node.js code from our .proto file. Finally, we’ll write a simple Node.js application that uses our service. With this guide, you’ll be up and running with gRPC in Node.js in no time! Step 1: Setup and Installation To start off, you’ll first need to install some dependencies for using gRPC in your project. Also, this article assumes that you have already installed and set up Node.js on your system. First, create a new folder, then set up a Node.js project with the following command: npm init -y Then, install the required gRPC dependencies with the npm package manager: npm install @grpc/grpc-js @grpc/proto-loader After this successfully installs, create three more files in the same directory called server.js, client.js, and service_def.proto. Step 2: Defining Our Service In this part, we will work with the service_def.proto file we created earlier. With this file, we can describe what our services are, which packages they use, and their input, output, and error types. The .proto file also implements the RPC methods and services for the client — it defines the messages that the server-side and client-side interact with. syntax = "proto3"; message Empty {} message User { string name = 1; int32 age = 2; } service UserService { rpc GetUser (Empty) returns (User) {} } Code Explanation Here we have defined a data model called User, with the two fields name and age. The UserService service is a collection(s) of signatures of the different actions this service has available and what they return in the response. Now that we have defined our services, the same configuration file can then be implemented using Go, Rust, Python, and Java without any required modifications! This is a compelling aspect of gRPC. You can define a profile with the RPCs and their interface, their input and output types, and any server-specific configuration. The client can then use the profile to generate stubs and skeletons of the client and server sides. The client and server can share the same profile and the same definitions of the RPCs. Now anyone can use that proto file to write the implementations of this service, but let’s make an implementation ourselves. Implementing the Service Now that we have defined our services, we can test them out by creating our own server and client program. Doing this lets us see how the server responds to a client request. Note: All of the following code is explained in the comments within the code. Step 3: Creating Server To create the server, open up the server.js file and load up the gRPC dependencies and the .proto file created earlier: //dependencies const grpc = require("@grpc/grpc-js"); const protoLoader = require("@grpc/proto-loader"); //path to our proto file const PROTO_FILE = "./service_def.proto"; Now let’s implement the service defined in our .proto file and its GetUser procedure: //options needed for loading Proto file const options = { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }; const pkgDefs = protoLoader.loadSync(PROTO_FILE, options); //load Definition into gRPC const userProto = grpc.loadPackageDefinition(pkgDefs); //create gRPC server const server = new grpc.Server(); //implement UserService server.addService(userProto.UserService.service, { //implment GetUser GetUser: (input, callback) => { try { callback(null, { name: "Jake", age: 25 }); } catch (error) { callback(error, null); } }, }); //start the Server server.bindAsync( //port to serve on "127.0.0.1:5000", //authentication settings grpc.ServerCredentials.createInsecure(), //server start callback (error, port) => { console.log(`listening on port ${port}`); server.start(); } ); Now that the server is running and listening for requests, we can define a client to send requests and communicate to the server. The client and the server are in separate files and are independent of one another. After running the server, you can run the client by typing in node client.js. Step 4: Creating a Client Now to create the client, the process would be similar to creating the server except for the last part. First, open up the client.js file and import the dependencies: //dependencies const grpc = require("@grpc/grpc-js"); const protoLoader = require("@grpc/proto-loader"); //path to our proto file const PROTO_FILE = "./service_def.proto"; Then create the client and make a request to the GetDog Procedure: //options needed for loading Proto file const options = { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }; const pkgDefs = protoLoader.loadSync(PROTO_FILE, options); //load Definition into gRPC const UserService = grpc.loadPackageDefinition(pkgDefs).UserService; //create the Client const client = new UserService( "localhost:3500", grpc.credentials.createInsecure() ); //make a call to GetUser client.GetUser({}, (error, user) => { if (error) { console.log(error); } else { console.log(user); } }); ` Output Now to see if everything actually works, open up two terminals — the first one starts the server using the following command: node server.js Then the second one runs the client with the following command: node client.js And you should see a result like this: Final Words That’s it. We’ve explained the basics of creating an API with gRPC and Node.js, how to build a service server, how to build a client that can find those services, and how to wire them together. To reiterate, the benefit of this approach is you can have one definition of a service (the .proto file), which can be implemented many times but work with the same client since all server implementations are bound by the profile. This approach also allows you to use a service implementation in multiple applications. For example, if you have a service that provides a user interface or back-end logic, you can use the same service implementation in multiple applications! This is particularly useful if you have an existing application that you want to enhance with new functionality. The latest API insights straight to your inbox