
If you develop APIs in Node.js, you probably breathe JSON and REST all day long. It's the standard, it's flexible, and it works everywhere.
But have you ever felt that your microservices were "chatting" a bit too slowly? Or that a simple typo in a JSON field name crashed your application in production?
That’s where gRPC comes in.
In this article, we’ll explore how this technology created by Google can transform your Node.js architectures, and we’ll build your first gRPC client/server together.
gRPC (gRemote Procedure Call) is an open-source framework that allows one service to communicate with another in an extremely fast and structured way.
Unlike REST, which sends text (JSON) over HTTP/1.1, gRPC uses:
HTTP/2: For performance (multiplexing, streaming).
Protocol Buffers (Protobuf): An ultra-lightweight binary format for data.
Speed: Binary messages are 7 to 10 times faster to serialize/deserialize than JSON.
Strong Typing: No more undefined errors. You define a strict contract. If you don't respect the contract, the code won't run.
Code Generation: You write the contract, and gRPC generates the client and server code for you (in JS, but also in Go, Python, Java, etc.).
Enough talk. Let's get our hands dirty. We are going to create a simple architecture:
A Server that holds news articles.
A Client that requests a specific news item.
Create a folder for your project and install the gRPC dependencies for Node.js:
mkdir grpc-tuto && cd grpc-tuto
npm init -y
npm install @grpc/grpc-js @grpc/proto-loaderNote: We are using
@grpc/grpc-js, which is the modern pure JavaScript implementation, replacing the old nativegrpclibrary.
This is the centerpiece. We are going to formally define what our data and functions look like in a file named news.proto.
Create the news.proto file at the root:
syntax = "proto3";
package news;
// 1. Définition du Service
service NewsService {
rpc GetNews (NewsRequest) returns (NewsResponse) {}
}
// 2. Définition des Messages (les données)
message NewsRequest {
string id = 1; // On envoie un ID
}
message NewsResponse {
string id = 1;
string title = 2;
string body = 3;
string image_url = 4;
}Unlike JSON, every field has a unique number (= 1, = 2). This is what allows for binary compression: we don't send the word "title" over the network, just the number 2.
Create a server.js file. This is where we will implement the logic.
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// 1. Chargement du fichier .proto
const packageDefinition = protoLoader.loadSync('news.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const newsProto = grpc.loadPackageDefinition(packageDefinition).news;
// Données fictives (mock)
const newsData = [
{ id: "1", title: "Node.js 20 est sorti", body: "Voici les nouveautés...", image_url: "img1.jpg" },
{ id: "2", title: "gRPC vs REST", body: "Le combat des chefs...", image_url: "img2.jpg" }
];
// 2. Implémentation de la méthode GetNews
function getNews(call, callback) {
const newsId = call.request.id;
const newsItem = newsData.find(n => n.id === newsId);
if (newsItem) {
// Succès : null en erreur, et l'objet en réponse
callback(null, newsItem);
} else {
// Erreur : on renvoie une erreur gRPC standard
callback({
code: grpc.status.NOT_FOUND,
details: "News introuvable"
});
}
}
// 3. Démarrage du serveur
function main() {
const server = new grpc.Server();
// On lie notre implémentation au service défini dans le proto
server.addService(newsProto.NewsService.service, { GetNews: getNews });
const address = '127.0.0.1:50051';
server.bindAsync(address, grpc.ServerCredentials.createInsecure(), () => {
console.log(`🚀 Serveur gRPC démarré sur ${address}`);
server.start(); // Facultatif selon les versions, mais explicite
});
}
main();Now, let's create a script to consume our API. Create client.js.
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// On charge le même fichier proto ! C'est le contrat partagé.
const packageDefinition = protoLoader.loadSync('news.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const newsProto = grpc.loadPackageDefinition(packageDefinition).news;
function main() {
// Création du client
const client = new newsProto.NewsService(
'127.0.0.1:50051',
grpc.credentials.createInsecure()
);
// Appel de la méthode
console.log("📡 Appel du service...");
client.GetNews({ id: "1" }, (error, response) => {
if (!error) {
console.log("✅ Réponse reçue du serveur :");
console.log(response);
} else {
console.error("❌ Erreur :", error.details);
}
});
}
main();Let's test it!
Open a terminal and start the server:
node server.js2. Open a second terminal and run the client:
node client.jsYou should see:
📡 Appel du service...
✅ Réponse reçue du serveur :
{
id: '1',
title: 'Node.js 20 est sorti',
body: 'Voici les nouveautés...',
image_url: 'img1.jpg'
}Try changing the ID in client.js to "99", and you will see the gRPC "News not found" error displayed cleanly.
Should you throw away all your Express REST APIs? Absolutely not.
Use gRPC if:
You are doing Microservices: Your Node.js backend services communicate with each other.
You need real-time Performance.
You work in a Polyglot team: A Node.js service needs to talk to a Python or Go service. The .proto file gets everyone on the same page.
Keep REST if:
You are building a public API for third parties.
Your API is consumed directly by a browser (React/Vue) without an intermediate proxy (although gRPC-Web exists, it adds complexity).
gRPC isn't just hype; it is a powerful tool to structure your backend communications. As a JS developer, the learning curve is short, and the gain in robustness is immediate.
So, are you ready to write your first .proto file?