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.