Back to Interview PrepGet interview ready with AILearn each topic the way interviewers ask it, then answer like you would in the interview.

Networking

gRPC

Overview
gRPC mind map: gRPC, RPC, ProtoBuf, HandsOn, Rest vs gRPC, Pros/Cons

What is gRPC

gRPC (Google Remote Procedure Call) is a framework that lets a client call functions on a remote server as if they were local functions.

Why use gRPC

  • Faster than REST (binary instead of JSON)
  • Strongly typed (uses .proto contracts)
  • Built for service-to-service communication

How it works

gRPC client (A) with client functions, client stub, RPC runtime, connecting over the network to server (B) with RPC runtime, server stub, server functions
  1. Client Function - You call a function like getUser(id).
  2. Client Stub - Acts like a proxy. Converts function call → Protocol Buffer (binary format).
  3. RPC Runtime - Sends request over network using HTTP/2.
  4. Server Stub - Converts binary data back to usable format. Calls server function.
  5. Server Function - Executes logic and returns response.
  6. Response path - Server → Server Stub → RPC Runtime → Client Stub → Client.

Key concept

  • Client Stub = like GraphQL resolver + API call layer
  • RPC Runtime = network layer
  • ProtoBuf = schema + data format

Protocol Buffers (ProtoBuf)

Protocol Buffers is a data format + contract system used by gRPC. It defines how data is structured and transferred between client and server.

Interface Definition Language (IDL)

ProtoBuf uses .proto files as an IDL. Example:

syntax = "proto3";

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
  int32 id = 1;
}

message UserResponse {
  string name = 1;
}

What this does:

  • Defines API methods (rpc GetUser)
  • Defines data structure (UserRequest, UserResponse)
  • Acts as a contract between client and server

Serialization / Deserialization

Serialization - Convert structured data → binary format. Happens at Client Stub.

{ id: 101 } → binary

Deserialization - Convert binary → structured data. Happens at Server Stub.

binary → { id: 101 }

Binary Support (Why it matters)

ProtoBuf uses binary encoding instead of JSON. Benefits:

  • Smaller size
  • Faster transfer
  • Less CPU usage
  • Strong typing (no parsing ambiguity)

Key Flow

JS Object
  → Serialize (ProtoBuf)
  → Binary (over network)
  → Deserialize
  → Server Object

Benefits of gRPC

High performance - Uses binary (ProtoBuf) instead of JSON → smaller payloads, faster communication.

Strong typing - .proto contracts ensure strict request/response structure → fewer runtime errors.

Code generation - Automatically generates client + server code from .proto → less manual work.

HTTP/2 support:

  • Multiplexing (multiple requests in one connection)
  • Header compression
  • Faster than HTTP/1.1

Streaming support:

  • Client streaming
  • Server streaming
  • Bidirectional streaming

Cross-language support - Works across multiple languages (Node.js, Java, Go, Python, etc.).

Better for microservices - Efficient internal communication between services.

Code and Architecture

HTTP client hits the Express server, which uses a gRPC client to call the gRPC server over 127.0.0.1:50051, which uses proto definitions and reads/writes an in-memory customer array

1. Initialize gRPC Server (setup)

npm init -y
npm install @grpc/grpc-js @grpc/proto-loader

2. customers.proto

syntax = "proto3";

message Empty {}

message CustomerRequestId {
  string id = 1;
}

message Customer {
  string id = 1;
  string name = 2;
  int32 age = 3;
  string address = 4;
}

message CustomerList {
  repeated Customer customer = 1;
}

service CustomerService {
  rpc GetAll (Empty) returns (CustomerList);
  rpc Get (CustomerRequestId) returns (Customer);
  rpc Insert (Customer) returns (Customer);
  rpc Update (Customer) returns (Customer);
  rpc Remove (CustomerRequestId) returns (Empty);
}

3. server/index.js

import { fileURLToPath } from "url";
import { dirname, join } from "path";

const __dirname = dirname(fileURLToPath(import.meta.url));
const PROTO_PATH = join(__dirname, "../customers.proto");

import grpc from "@grpc/grpc-js";
import protoLoader from "@grpc/proto-loader";

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  arrays: true,
});

const customersProto = grpc.loadPackageDefinition(packageDefinition);

const server = new grpc.Server();

const customers = [
  { id: "sfdjkfjsdf", name: "John Doe", age: 30, address: "123 Main St" },
  { id: "fw2342df", name: "Jane Smith", age: 25, address: "456 Oak Ave" },
  { id: "sdf2342", name: "Bob Johnson", age: 40, address: "789 Pine Rd" }
];

server.addService(customersProto.CustomerService.service, {
  getAll: (call, callback) => {
    callback(null, { customer: customers });
  },
  get: (call, callback) => {
    const customer = customers.find((c) => c.id === call.request.id);
    if (customer) {
      callback(null, customer);
    } else {
      callback({ code: grpc.status.NOT_FOUND, details: "Customer not found" });
    }
  },
  insert: (call, callback) => {
    const newCustomer = {
      id: Math.random().toString(36).substring(2, 15),
      name: call.request.name,
      age: call.request.age,
      address: call.request.address,
    };
    customers.push(newCustomer);
    callback(null, newCustomer);
  },
  update: (call, callback) => {
    const index = customers.findIndex((c) => c.id === call.request.id);
    if (index !== -1) {
      customers[index] = {
        id: call.request.id,
        name: call.request.name,
        age: call.request.age,
        address: call.request.address,
      };
      callback(null, customers[index]);
    } else {
      callback({ code: grpc.status.NOT_FOUND, details: "Customer not found" });
    }
  },
  remove: (call, callback) => {
    const index = customers.findIndex((c) => c.id === call.request.id);
    if (index !== -1) {
      const removedCustomer = customers.splice(index, 1)[0];
      callback(null, removedCustomer);
    } else {
      callback({ code: grpc.status.NOT_FOUND, details: "Customer not found" });
    }
  },
});

server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), (err, port) => {
  if (err) {
    console.error("Failed to bind server:", err);
  } else {
    console.log(`Server bound to port ${port}`);
    server.start();
  }
});

4. client/client.js

import { fileURLToPath } from "url";
import { dirname, join } from "path";

const __dirname = dirname(fileURLToPath(import.meta.url));
const PROTO_PATH = join(__dirname, "../customers.proto");

import grpc from "@grpc/grpc-js";
import protoLoader from "@grpc/proto-loader";

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true,
  longs: String,
  enums: String,
  arrays: true,
});

const CustomerService = grpc.loadPackageDefinition(packageDefinition);

export const client = new CustomerService.CustomerService(
  "127.0.0.1:50051",
  grpc.credentials.createInsecure()
);

5. client/index.js

import express from "express";
import { client } from "./client.js";

const app = express();
const PORT = 3005;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/', (_req, res) => res.json({ message: "Root is groot" }));

app.get('/customers', (req, res) => {
  client.getAll({}, (err, response) => {
    if (err) {
      console.error(err);
      res.status(500).json({ error: "Internal Server Error" });
    } else {
      res.json(response);
    }
  });
});

app.post('/customers', (req, res) => {
  const { id, name, age, address } = req.body;
  client.insert({ id, name, age, address }, (err, response) => {
    if (err) {
      console.error(err);
      res.status(500).json({ error: "Internal Server Error" });
    } else {
      res.json(response);
    }
  });
});

// more routes can be added here similarly for update and delete operations

app.listen(PORT, () => console.log(`Client Started at port ${PORT}`));

Flow

client/index.js → client.js → gRPC call → server/index.js → response
Terminal showing GET /customers pretty-printed JSON response with three customer records

REST vs gRPC

FeatureRESTgRPC
Communication ProtocolHTTP/1.1 or HTTP/HTTPSHTTP/2 (default)
Message FormatJSON / XML (text-based)Protocol Buffers (binary)
IDL (Interface Definition Language)No strict standard (OpenAPI / Swagger / WSDL).proto files (strict contract)
Data SerializationText-based (JSON, XML)Binary (ProtoBuf)
EfficiencySlower (larger payloads, multiple requests)Faster (compact + multiplexing in HTTP/2)
FlexibilityMore flexible (loose schema)Strict typing (strong contract)
RPC StylesRequest-Response (GET, POST, etc.)Unary, Client Streaming, Server Streaming, Bidirectional Streaming
StreamingLimited (WebSockets / SSE workaround)Native streaming support
PerformanceModerateHigh performance
Use CasePublic APIs, web appsMicroservices, real-time systems
Learning CurveEasySlightly steep

gRPC: Advantages vs Disadvantages

AdvantagesDisadvantages
High performance (binary + HTTP/2)Not browser-friendly (needs gRPC-Web)
Smaller payload size (ProtoBuf)Harder to debug (binary data)
Strong typing with .proto contractsLearning curve (IDL + tooling)
Supports streaming (bi-directional)Limited human readability
Efficient for microservices communicationLess flexible than REST
Built-in code generationRequires schema management
You're viewing a free lesson. Sign up free to track your progress and unlock the full course.Sign up freeSign in