gRPC Cheatsheet
Client
Use this gRPC reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Creating a Client Stub
const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const packageDef = protoLoader.loadSync('./protos/user.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }); const { myapp: { v1: proto } } = grpc.loadPackageDefinition(packageDef); // The stub wraps a channel; all methods are auto-generated const client = new proto.UserService( 'localhost:50051', grpc.credentials.createInsecure(), );
Client Credentials
// Insecure (plaintext) const creds = grpc.credentials.createInsecure(); // TLS with system CA roots const creds = grpc.credentials.createSsl(); // TLS with custom CA const creds = grpc.credentials.createSsl(fs.readFileSync('ca.crt')); // mTLS const creds = grpc.credentials.createSsl( fs.readFileSync('ca.crt'), fs.readFileSync('client.key'), fs.readFileSync('client.crt'), ); // TLS + per-call token (combined) const tlsCreds = grpc.credentials.createSsl(); const callCreds = grpc.credentials.createFromMetadataGenerator((params, cb) => { const meta = new grpc.Metadata(); meta.add('authorization', `Bearer ${getAccessToken()}`); cb(null, meta); }); const combined = grpc.credentials.combineChannelCredentials(tlsCreds, callCreds); const client = new proto.UserService('api.example.com:443', combined);
Channel Options
const client = new proto.UserService('localhost:50051', creds, { 'grpc.max_receive_message_length': 4 * 1024 * 1024, 'grpc.max_send_message_length': 4 * 1024 * 1024, 'grpc.keepalive_time_ms': 30_000, 'grpc.keepalive_timeout_ms': 10_000, 'grpc.keepalive_permit_without_calls': 1, 'grpc.enable_retries': 1, // Load balancing 'grpc.service_config': JSON.stringify({ loadBalancingConfig: [{ round_robin: {} }], }), });
Deadlines
// Deadline is an absolute Date object (not a duration) const deadline = new Date(Date.now() + 5000); // 5 seconds from now client.getUser({ user_id: 1 }, { deadline }, (err, response) => { if (err?.code === grpc.status.DEADLINE_EXCEEDED) { console.error('timed out'); return; } console.log(response); });
Metadata (Headers)
const meta = new grpc.Metadata(); meta.add('authorization', 'Bearer eyJhbG...'); meta.add('x-request-id', 'abc-123'); meta.set('x-single-value', 'only-one'); // set replaces all values for the key // Binary metadata — key must end with -bin meta.add('trace-bin', Buffer.from([0x01, 0x02])); // Pass as second arg (before callback / options) client.getUser({ user_id: 1 }, meta, (err, res) => { /* ... */ });
Promisify a Unary Call
const { promisify } = require('util'); // Promisify individual methods const getUser = promisify(client.getUser.bind(client)); const user = await getUser({ user_id: 1 }); // Generic wrapper function callAsync(method, request, metadata = new grpc.Metadata()) { return new Promise((resolve, reject) => { method.call(client, request, metadata, (err, res) => { if (err) reject(err); else resolve(res); }); }); } const res = await callAsync(client.getUser, { user_id: 1 });
Cancelling a Call
// Every call returns a ClientUnaryCall / ClientReadableStream / etc. // All have .cancel() const call = client.getUser({ user_id: 1 }, (err, res) => { if (err?.code === grpc.status.CANCELLED) { console.log('cancelled by client'); return; } console.log(res); }); setTimeout(() => call.cancel(), 1000); // cancel after 1s
Reading Metadata from Responses
const call = client.getUser({ user_id: 1 }, (err, res) => { console.log(res); }); call.on('metadata', (metadata) => { // initial headers from server console.log('header request-id:', metadata.get('request-id')); }); call.on('status', (status) => { // final status + trailing metadata console.log('trailing metadata:', status.metadata.getMap()); });
Wait for Ready
// waitForReady — useful after creating client before first call // deadline: Date object client.waitForReady(new Date(Date.now() + 10_000), (err) => { if (err) throw err; console.log('channel is ready'); });
Channel State
const grpc = require('@grpc/grpc-js'); const state = client.getChannel().getConnectivityState(true); // true = try to connect // States: IDLE(0) CONNECTING(1) READY(2) TRANSIENT_FAILURE(3) SHUTDOWN(4) client.getChannel().watchConnectivityState(state, deadline, (err) => { // fires when state changes or deadline passes const newState = client.getChannel().getConnectivityState(false); console.log('new state:', newState); });
Closing the Client
// Closes the underlying channel — no more calls possible client.close();
Load Balancing
// DNS round-robin (multiple A records) const client = new proto.UserService('dns:///user-service.default.svc.cluster.local:50051', creds, { 'grpc.service_config': JSON.stringify({ loadBalancingConfig: [{ round_robin: {} }], }), }); // Multiple addresses (pick_first by default) // Use xDS / service mesh for production load balancing
Retry Policy
const serviceConfig = { methodConfig: [ { name: [{ service: 'myapp.v1.UserService', method: 'GetUser' }], retryPolicy: { maxAttempts: 4, // includes first attempt initialBackoff: '0.1s', maxBackoff: '1s', backoffMultiplier: 2.0, retryableStatusCodes: ['UNAVAILABLE', 'RESOURCE_EXHAUSTED'], }, }, { name: [{ service: 'myapp.v1.UserService' }], // wildcard: all methods timeout: '5s', // default deadline }, ], }; const client = new proto.UserService('localhost:50051', creds, { 'grpc.enable_retries': 1, 'grpc.service_config': JSON.stringify(serviceConfig), });
Hedging Policy
// Hedging sends the RPC to multiple backends simultaneously // and uses the first successful response const serviceConfig = { methodConfig: [{ name: [{ service: 'myapp.v1.UserService', method: 'GetUser' }], hedgingPolicy: { maxAttempts: 3, hedgingDelay: '0.05s', // 50ms before sending next hedge nonFatalStatusCodes: ['UNAVAILABLE'], }, }], };
Common Gotchas
Channel reuse — create one client (channel) per service and reuse it for the lifetime of the process. Creating a new client per request is expensive and leaks channels.
Deadline is a Date, not a duration —
new Date(Date.now() + 5000)not5000.
Error code on timeout —
DEADLINE_EXCEEDEDwhen the client-side deadline fires;CANCELLEDwhen you call.cancel().
waitForReady— without this, calls made immediately after construction may fail withUNAVAILABLEif the server is not yet reachable. Use in service startup health checks.
Binary metadata keys — must end with
-bin; values areBuffer. All other keys are strings.