What is gRPC?

gRPC: Google Remote Procedure Call is a Google-developed open-source framework. The gRPC lets you define REQUEST AND RESPONSE for Remote Procedure Call and lessens your struggle by handling the rest. Features of gRPC:

  • Modern
  • Fast
  • Has low latency
  • Efficient Load balancing
  • Supports streaming
  • Plugin authentication
  • Monitors data
  • Build on top of HTTP/2
  • Lanaguage independent

Tutorial Goal: Implement gRPC services in NodeJS

We all know how popular and successful NodeJs is. So, in this tutorial, we will implement gRPC services in NodeJs. We will build a demo application and perform a CRUD operation. So without further ado let’s get started with the implementation of gRPC services in our NodeJS application.

Bacancy will lessen your development struggles. Trust the best! Trust us!
Get in touch with Bacancy, if you are looking for potential NodeJS developers for your dream project. We have highly skilled developers with great problem-solving approaches. Don’t think much! Just contact us and hire NodeJs developer from us!

Steps to Implement gRPC Services in NodeJS

Initialize Node environment

Copy Text
npm init -y

Install gRPC server

Copy Text
npm install --save grpc @grpc/proto-loader uuid express body-parser

grpc: It will install the gRPC server
/proto-loader: It will load protobuf file
uuid: It will create random hash ids for students

Now, create students.proto, server.js, and client.js files. A single student item will have- id, name, age, and coursename.

Copy Text
syntax = "proto3";
message Student {
    string id = 1;
    string name = 2;
    int32 age = 3;
    string CourseName = 4;
}

The Students defines our students request message format, where each news request has a unique id, name of the students, age of the students, and course name taken by students.

Define the StudentService in proto

Create an RPC service. All CRUD operations will be available in this service named StudentsService.

Copy Text
...
service StudentsService {
    rpc GetAllStudents (Empty) returns (NewsList) {}
}

message Empty {}
message StudentsList {
   repeated Students students = 1;
}

The entire code of news.proto file is shown below.

Copy Text
syntax = "proto3";
service StudentService {
    rpc GetAllNews (Empty) returns (NewsList) {}
}

message Student {
    string id = 1;
    string name = 2;
    int32 age = 3;
    string CourseName = 4;
}

message Empty {}

message StudentsList {
   repeated Student students = 1;
}

const grpc = require("@grpc/grpc-js");
const PROTO_PATH = "./news.proto";
var protoLoader = require("@grpc/proto-loader");

const options = {
  longs: String,
  oneofs: true,
  keepCase: true,
  enums: String,
  defaults: true,
};

var packageDefinition =protoLoader.loadSync(PROTO_PATH, options);

const newsProto=grpc.loadPackageDefinition(packageDefinition);

const {v4:uuidv4}=require(“uuid”)
const server = new grpc.Server();

let Students = [
  { 
id: "a68b823c-7ca6-44bc-b721-fb4d5312cafc",
name: "John Bolton", 
age: 22, 
courseName: "Course 1"
  },
  { 
id: "34415c7c-f82d-4e44-88ca-ae2a1aaa92b7",
name: "John Bolton", 
age: 22, 
courseName: "Course 2"
  },  
];

server.addService(StudentsProto.StudentService.service, {
  getAll: (_, callback) => {
    callback(null, {Students});
  },
});

server.bind("127.0.0.1:30043",grpc.ServerCredentials.createInsecure());
console.log("Server running at http://127.0.0.1:50051");
server.start();

First of all, we have imported @grpc/grpc-js and @grpc/proto-loader libraries. The const variable named PROTO PATH will save the location of students.proto file. Later, loadSync method will load the proto file into gRPC. And loadPackageDefinition method will load the package definition. After that, to initialize the server instance () we will call new grpc.server

Create Client stub

We will call the getAll method defined in Server’s addService method as the second parameter from the client, as shown below.

Copy Text
const PROTO_PATH = "./students.proto";
const grpc = require("@grpc/grpc-js");
var protoLoader = require("@grpc/proto-loader");

var packageDefinition=protoLoader.loadSync(PROTO_PATH, {
  longs: String,
  oneofs: true,
  keepCase: true,
  enums: String,
  defaults: true,
});

const StudentService = grpc.loadPackageDefinition(packageDefinition).StudentService;

const client = new StudentService(
  "localhost:30043",
  grpc.credentials.createInsecure()
);

getAll Method: Get all Students

Call the getAll method in the server from the client variable.

Copy Text
...
const client = new Service(
  "localhost:30043",
  grpc.credentials.createInsecure()
);


client.getAll(null, (err, data) => {
  if (!err) throw err
    console.log(data);
});

Now execute the client.js code as follows:

Copy Text
// node client.js

{
  Students: [
    { 
id: "a68b823c-7ca6-44bc-b721-fb4d5312cafc",
name: "John Bolton", 
age: 22, 
courseName: "Course 1"
  },
  { 
id: "34415c7c-f82d-4e44-88ca-ae2a1aaa92b7",
name: "John Bolton", 
age: 22, 
courseName: "Course 2"
  },
 ]
}

We have successfully executed a gRPC service method from a gRPC client. After exporting the NewsService instance you can import it in another file for calling methods.

Copy Text
const PROTO_PATH = "./students.proto";
const grpc = require("@grpc/grpc-js");
var protoLoader = require("@grpc/proto-loader");

var packageDefinition=protoLoader.loadSync(PROTO_PATH, {
  longs: String,
  oneofs: true,
  keepCase: true,
  enums: String,
  defaults: true,
});

const StudentService = grpc.loadPackageDefinition(packageDefinition).StudentService;

const client = new StudentService(
  "localhost:30043",
  grpc.credentials.createInsecure()
);

module.exports = client;

We may now import the client into a different file. We’ll make a separate file for each step we wish to take. We’ll make a file called get test.js that imports the client and calls the getAll method.

Copy Text
// test.js

const client = require("./client");

client.getAll(null, (err, data) => {
  if (!err) throw err
    console.log(data);
});

Insert a Student record

For inserting data create a new method. Open the proto file and under services, you can add an RPC with the name of the new method. Here, the name of our method is Insert under StudentService service as shown below.

Copy Text
...
service StudentService {
    rpc GetAll (Empty) returns (StudentList) {}
    rpc Insert (Student) returns (Student) {}
}
...

The service will accept the Student message and return the new Student object.

Copy Text
...
server.addService(StudentsProto.StudentService.service, {
  getAll: (_, callback) => {
    callback(null, {Students});
  },


  insert: (call, callback) => {
    let Student = call.request;
    Student.id = uuidv4();
    Students.push(Student);
    callback(null, news);
  },
});
...

Delete a Student

Now, we will code for deleting a Student. Here’s the code snippet for the same.

Copy Text
...
service StudentService {
    rpc GetAll (Empty) returns (StudentList) {}
    rpc Insert (Student) returns (Student) {}
    rpc Remove (Student) returns (Student) {}
}

message StudentRequestId {
    string id = 1;
}
...

Here, the request is StudentRequestId which will respond with an empty message.

The code snippet from server.js is shown below.

Copy Text
...
  remove: (call, callback) => {
    let existingStudentIndex = Students.findnidex(
		n =>n.id == call.request.id
);
If(existingStudentIndex != -1){
	Students.splice(existingStudentIndex,1)
	callback(null,{});
    }
},
...

Update an existing Student

To update the data we will add a method in the proto file.

Copy Text
...
service StudentService {
    rpc GetAll (Empty) returns (StudentList) {}
    rpc Insert (Student) returns (Student) {}
    rpc Remove (Student) returns (Student) {}
    rpc Update (Student) returns (Student) {}

}

...

The method is accepting a Student message and responding with the edited News object. The code for updating the student data is shown below.

Copy Text
...
  update: (call, callback) => {
    let existingStudent = Students.find(n=> n.id== call.request.id);

	if(existingStudent){

		existingStudent.name = call.request.name;
		existingStudent.age = call.request.age;
		existingStudent.CourseName =call.request.CourseName;

		callback(null, existingStudent);

},
...

Get a Student

Let’s set a method in the proto file:

Copy Text
...
service StudentService {
    rpc GetAll (Empty) returns (StudentList) {}
    rpc Get (Student) returns (Student) {}
    rpc Insert (Student) returns (Student) {}
    rpc Remove (Student) returns (Student) {}
    rpc Update (Student) returns (Student) {}

}
...

The Get method requires an ID as the request message and returns a Student message. Here’s the implementation in the server.js file:

Copy Text
...
  get: (call, callback) => {
    let Student = Students.find(n=>n.id == call.request.id)

    if(Student) = {
    callback(null, Student);
}	
  },
...

We get the id from the call param object. The id is used to retrieve the corresponding student item from the Students array. The callback function is called with the retrieved student item passed as param, this makes the client gets the student item.

Copy Text
// test.js
// get all news
const client = require("./client");

client.getAll({}, (error, students) => {
  if (error) throw error;
  console.log(students);
});

//add a students
client.insert(
  {
    name: "Title news 3",
    age: 11,
    CourseName: "Image URL here",
  },
  (err, students) => {
    if (err) throw err;
    console.log("Successfully created a students.");
  }
);

// edit a student
client.update(
  {
    id: "a68b823c-7ca6-44bc-b721-fb4d5312cafc",
    name: "Title news 3",
    age: 11,
    CourseName: "Image URL here",
  },
  (err, students) => {
    if (err) throw err;
    console.log("Successfully Updated a Students.");
  }
);

// delete a students
client.remove(
  {
    id: "34415c7c-f82d-4e44-88ca-ae2a1aaa92b7",
  },
  (err, students) => {
    if (err) throw err;
    console.log("Successfully deleted students record.");
  } 
);

Now, run the file:

Copy Text
node test

With Http server

We now have a server, proto, and client all built and ready. Attach a Node server to the client.js so an endpoint in our server will call the procedure in the gRPC news service. Here are the endpoints.

  • /GET endpoint will call the getAll sub-routine to get all the students in the database.
  • /save POST endpoint will call the insert sub-routine to create a new student item.
  • /update PUT will call the update sub-routine to edit/update a student item.
  • /remove DELETE will call the delete sub-routine to delete a student item.

Here’s the code in Nodejs:

Copy Text
const client = require("./client");

const express = require("express");
const bodyParser = require("body-parser");

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.get("/", (req, res) => {
	client.getAll(null, (err, data) => {
		if (!err) {
			res.send(data.Students)
		}
	});
});

app.post("/save", (req, res) => {
	console.log(req.body.CourseName)
	let newStudent = {
		name: req.body.name,
		age: req.body.age,
		CourseName: req.body.CourseName
	};

	client.insert(newStudent, (err, data) => {
		if (err) throw err;
res.send({data:data, msg:"Student created successfully"})

	});
});

app.post("/update", (req, res) => {
	const updateStudent = {
		id: req.body.id,
		name: req.body.name,
		age: req.body.age,
		CourseName: req.body.CourseName
	};

	client.update(updateStudent, (err, data) => {
		if (err) throw err;

		res.send({msg:"Student updated successfully"})
	});
});

app.post("/remove", (req, res) => {
	client.remove({ id: req.body.Student_id }, (err, _) => {
		if (err) throw err;

		console.log("Student removed successfully");
		//res.redirect("/");
		res.send({msg:"Student removed successfully"})
	});
});

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

Conclusion

So, this was about how can we implement gRPC services in NodeJS. I hope your purpose of landing on this tutorial has served you well. If you are a NodeJS enthusiast then feel free to visit the NodeJS tutorials page and learn more about Node. We are always open to suggestions and feedback. Write us back if you have any questions. Bacancy has best NodeJS developers with basic and advanced knowledge; contact us to hire NodeJs developers for your application.

Looking for a Node.js Development Company?

Build scalable web apps with top Node.js developers.

Connect Now

Get In Touch

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.