gRPC Services

gRPC is a high-performance way for services to call each other. You define a service contract once in a Protocol Buffers .proto file, and both the server and client speak it over HTTP/2 with compact binary messages instead of JSON.

Learn gRPC Services in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll write a .proto definition, then build a Node.js server and client with @grpc/grpc-js and @grpc/proto-loader , covering both unary and streaming RPCs — and see why teams pick gRPC over REST for internal traffic.

What You'll Learn in This Lesson

1️⃣ The Contract: a .proto File

Everything in gRPC starts with the contract . A .proto file declares services (groups of RPCs) and messages (typed records). Each message field gets a unique number — that tag, not the name, is what travels on the wire, which is why Protobuf is so compact.

Notice the stream keyword on Countdown 's return type — that's what makes it a server-streaming RPC. The same file is shared by server and client, so both always agree on the shapes.

2️⃣ Building the Server

Load the .proto with proto-loader , implement each RPC, register them with addService , and bind to a port. A unary handler answers with callback(null, response) ; a streaming handler calls call.write(...) repeatedly and then call.end() .

The error-first callback(error, response) is the standard Node convention: pass null as the error on success, or a grpc.status error to signal failure. The client receives whichever you send.

3️⃣ Calling It from a Client

The client loads the same .proto and creates a stub — an object whose methods look local but actually make remote calls. A unary call passes a request and reads the response in a callback; a streaming call returns an event-emitting stream you read with on("data") and on("end") .

Because the contract is shared, the stub already knows the exact request and reply shapes. No URL parsing, no manual JSON — you call client.sayHello(...) like a function and get a typed reply back.

Your turn. Fill in the two ___ blanks to complete a working unary round-trip.

Extend the contract with a new server-streaming RPC, for example rpc Fibonacci (CountRequest) returns (stream CountReply); . Implement the handler so it call.write() s each Fibonacci number up to the requested count, then call.end() s. On the client, read it with stream.on("data", ...) . Confirm the values arrive one at a time over a single HTTP/2 connection — that's the streaming power that REST can't match cleanly.

📋 Quick Reference — gRPC in Node

Practice quiz

What does gRPC use to define services and message types?

  • Protocol Buffers (.proto files)
  • OpenAPI/Swagger YAML
  • JSON Schema
  • GraphQL SDL

Answer: Protocol Buffers (.proto files). gRPC uses Protocol Buffers .proto files as the contract for services and messages.

Which transport protocol does gRPC run over?

  • HTTP/1.1
  • WebSocket
  • HTTP/2
  • raw TCP with no framing

Answer: HTTP/2. gRPC is built on HTTP/2, which enables multiplexing and streaming over one connection.

In a .proto file, how do you give each field its position on the wire?

  • Field positions are automatic and invisible
  • By a unique field number after the equals sign, like name = 1
  • By its order in the file only
  • By a JSON key

Answer: By a unique field number after the equals sign, like name = 1. Each field has an explicit tag number (name = 1) that identifies it in the binary encoding.

Which Node.js packages are commonly used to run gRPC?

  • axios and ws
  • amqplib and protobufjs only
  • express and body-parser
  • @grpc/grpc-js and @grpc/proto-loader

Answer: @grpc/grpc-js and @grpc/proto-loader. @grpc/grpc-js is the pure-JS gRPC library; @grpc/proto-loader loads .proto files at runtime.

What is a unary RPC?

  • A single request that returns a single response
  • A single request that returns a stream of responses
  • A stream of requests returning a single response
  • A stream of requests returning a stream of responses

Answer: A single request that returns a single response. Unary is the simplest RPC: one request message in, one response message out.

Which RPC type lets a server push many messages for one request, like a live feed?

  • Client streaming
  • Server streaming
  • There is no streaming in gRPC
  • Unary

Answer: Server streaming. Server streaming sends one request and the server returns a stream of response messages.

On the server, how do you attach a method implementation to a service?

  • channel.consume(method, handler)
  • server.use(handler)
  • app.get('/method', handler)
  • server.addService(serviceDef, { methodName: handler })

Answer: server.addService(serviceDef, { methodName: handler }). You register handlers with server.addService(serviceDefinition, implementationObject).

In a unary handler, how do you return a successful response to the client?

  • throw responseObject
  • ctx.send(responseObject)
  • callback(null, responseObject)
  • return res.json(value)

Answer: callback(null, responseObject). Node gRPC unary handlers use a callback: callback(error, response), with null error on success.

Compared with REST and JSON, what is a key advantage of gRPC?

  • It is easier to call from a browser address bar
  • Compact binary Protobuf messages and a strongly typed, code-generated contract
  • It requires no schema at all
  • It only works over HTTP/1.1

Answer: Compact binary Protobuf messages and a strongly typed, code-generated contract. Protobuf is smaller and faster to parse than JSON, and the .proto gives a typed, generated contract.

What does proto-loader's loadSync followed by loadPackageDefinition produce?

  • A usable gRPC object with the service constructors and client stubs
  • A raw JSON string of the proto
  • An HTTP route table
  • A database schema

Answer: A usable gRPC object with the service constructors and client stubs. loadSync parses the .proto and loadPackageDefinition turns it into callable service/client objects.