Building a secure API with gRPC using Node

This article will illustrate the utilization of gRPC through a client-server communication model. For contemporary applications, gRPC stands out as an excellent option due to its robust support for diverse data types. It particularly excels in handling substantial data, such as streaming data.

Establishing the Node.js Project

To begin, organize the application’s directory structure by creating a folder named “GRPC-DEMO” and initiate a Node.js project using npm with the following commands:

$ mkdir GRPC-DEMO
$ cd GRPC-DEMO
$ npm init -y

Installing packages

Navigate to your application’s root directory using the terminal. Install the required packages by executing the npm install command, as demonstrated in the following code snippet:

$ npm install express @grpc/grpc-js @grpc/proto-loader

Now, let’s review the packages installed in the preceding code snippet:

  1. Express: This serves as your application’s HTTP server.
  2. @grpc/grpc-js: A gRPC library designed for Node.js, allowing the creation of gRPC services within the Node.js runtime.
  3. @grpc/proto-loader: This package is essential for loading protobuf files for gRPC usage. It leverages the version 3 package of protobuf.js.

package.json file will look similar to the code snippet below:

{
  "name": "grpc-demo",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@grpc/grpc-js": "^1.9.13",
    "@grpc/proto-loader": "^0.7.10",
    "express": "^4.18.2"
  }
}

Defining the Protocol Buffer Structure


This blog demonstrates the implementation of gRPC in a straightforward students record application. The showcased application receives student’s details and stores them in an in-memory database, enabling functionalities like fetching, and inserting student’s data.

In gRPC applications, communication between diverse applications is facilitated through a protobuf file containing the service interface and required payloads. These protobuf files typically carry a .proto extension, as outlined in our project setup schema.

To proceed, create an student.proto file in the root directory of your application and insert the following code. You can refer to the project structure schema we established earlier for guidance.

syntax="proto3";

service StudentService {
   rpc GetAllStudents (Empty) returns (StudentList) {}
   rpc GetStudent (StudentId) returns (Student) {}
   rpc CreateStudent (Student) returns (Student) {}
}

message Empty {}

message Student {
   int32  id = 1;
   string name = 2;
   int32 age = 3;
   string course = 4;
   string status = 5;
}

message StudentList {
   repeated Student students = 1;
}

message StudentId {
   int32 id = 1;
}

In the provided proto definition code snippets, the following steps were taken: we initially specified the protocol buffer version using the syntax = "proto3" definition, followed by the definition of the protocol service.

Subsequently, within the protocol event service description, we established a service named StudentService. Here, we created rpc functions within the service, specifying their required parameters and expected return values. While it’s possible to define multiple services according to your application’s requirements, for simplicity, we’ve limited our definition to just one.

Furthermore, we outlined the data types for the rpc functions within the StudentService definition and indicated the return values using the gRPC unique field numbering system. This numbering system is integral to describing the number of bytes utilized during encoding.

Creating the gRPC server

Adhering to the previously established folder structure, create an “server.js” file in root directory. Copy and paste the following code snippet into the newly created “server.js” file:

const grpc= require("@grpc/grpc-js");
const protoLoader=require("@grpc/proto-loader");

let packageDefinition = protoLoader.loadSync("student.proto", {
   keepCase: true,
   longs: String,
   enums: String,
   arrays: true
});
let studentsProto = grpc.loadPackageDefinition(packageDefinition);
const students = [
   {
       id: 1,
       name: "BBB",
       age: 20,
       course: "B.Com",
       status: "Persuing"
   },
   {
       id: 2,
       name: "CCC",
       age: 21,
       course: "B.Sc",
       status: "Completed"
   }
];
const server = new grpc.Server();
server.addService(studentsProto.StudentService.service, {

   GetAllStudents: (_, callback) => {
       callback(null, { students });
   },

   GetStudent: (call, callback) => {
       let student = students.find(n => n.id == call.request.id);

       if (student) {
           callback(null, student);
       } else {
           callback({
               code: grpc.status.NOT_FOUND,
               details: "Student Not found"
           });
       }
   },
   CreateStudent : (call,callback) =>{
    const newStudent=call.request;
    if(newStudent){
        students.push(newStudent);
        callback(null,newStudent);
    } else{
        callback({
            code : grpc.status.NOT_FOUND,
            details :"Student's details not added..."
        });
    }
   }
});
server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), (error, port) => {
console.log(`Server listening at http://127.0.0.1:${port}`);
server.start();
});
  • In the provided code snippet, we brought in the “student.proto” file, defined earlier. Subsequently, we utilized the loadSync method from the protoLoader library to load the protobuf file synchronously. The resulting protobuf definitions were stored in the studentsProto variable, encompassing all the proto definitions for our gRPC service.
  • In the above code snippet, the addService method was invoked on the gRPC server instance to register the application services, specifically handling create, read, and update operations on Students.

Creating the gRPC client

create a files in the root folder an client.js file and an app.js file. Paste the following code snippet into the client.js file.

const grpc= require("@grpc/grpc-js");
const protoLoader=require("@grpc/proto-loader");

let packageDefinition = protoLoader.loadSync("student.proto", {
   keepCase: true,
   longs: String,
   enums: String,
   arrays: true
});
let StudentService = grpc.loadPackageDefinition(packageDefinition).StudentService;

const client = new StudentService("127.0.0.1:50051", grpc.credentials.createInsecure());
module.exports = client;

In the provided code snippet, we imported the previously crafted proto definitions, loaded them using protoLoader, connected the gRPC client to the server application’s IP address, and exported the StudentService with the variable name client.
Now Server & Client is ready for communications.

Creating the HTTP Server

We will make request using http to the created grpc server through grpc clients. for that create a index.js file and paste below code snippet:

const client = require("./client");
const express = require("express");
const app = express();
app.disable('x-powered-by');
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", (req, res) => {
   client.GetAllStudents(null, (err, data) => {
       if (!err) {
           res.status(200).send(JSON.stringify(data));
       }
   });
});
app.get("/student", (req, res) => {
    const request = { id: req.query.id };
   client.GetStudent(request, (err, data) => {
       if (!err) {
           res.status(200).send(
               JSON.stringify(data)
           );
       } else{
        res.status(500).send({
            "msg" :"Someting wrong here.."
        });
       }
   });
});
app.post("/createStudent",(req,res)=>{
    const studentDetail={
        id:req.body.id,
        name:req.body.name,
        age:req.body.age,
        course:req.body.course,
        status:req.body.status
    };
    client.CreateStudent(studentDetail,(err,data)=>{
        if(!err){
            res.status(200).send(JSON.stringify(data));
        }else{
        res.status(500).send({
            "msg" :"Someting wrong here.."
        });
       }
    });
})

const PORT = process.env.PORT || 50050;
app.listen(PORT, () => {
    console.log("Server running at port %d", PORT);
});

Examining the above code snippet, we imported the student-service from the client.js file. Subsequently, we configured an Express server with straightforward endpoints responsible for handling the creation, fetch operations on Students. These operations are executed by making remote calls to the server application using gRPC techniques.

Project Setup is below:

building web apis with grpc

Verify the Server and Client Applications

1-Start grpc Server:

building web apis with grpc

The server application is now live on http://localhost:50051:

2-Start http server:

building web apis with grpc

To conduct a test, open your browser and navigate to localhost:50050 or utilize an API testing tool like Postman. You should observe the default student that was initially added to our students array. Your expected response should mirror the screenshot provided below:

Test for GetAllStudents: fetching all student’s information.

building web apis with grpc

Test for GetStudent: fetching student information by student id.

building web apis with grpc

Test for CreateStudent: inserting student information into in-memory db’s.

building web apis with grpc

You built a secure API with gRPC through http.

Leave a Reply

Your email address will not be published. Required fields are marked *