Skip to Content
🎉 gRPCity 3.0 is released. Read more →
DocumentationAdvancedServer Middleware

Server Middleware

Server middleware wraps every inbound RPC, before and after the handler runs. Same idea as client middleware, different context — here you can read inbound metadata, mutate the response, attach trailing metadata, or convert thrown errors into a status.

Onion model

gRPCity uses Koa-style onion-model middleware on the server side too: each function decides when to call next(), and code on either side of next() runs around everything below it.

Three middlewares a, b, c registered in that order produce this trace:

enter a enter b enter c rpc-method() exit c exit b exit a

Usage

gRPCity server provides the server.use() method for adding middleware.

The current version only supports middleware for async unary and async stream methods, and stream and callback are not yet supported.

Usage:

const server = await loader.initServer() // Add one by one server.use(f1) server.use(f2) server.use(f3) // Add with multiple parameters server.use(f1, f2, f3) // Add using an array server.use([f1, f2, f3])

The placement of adding middleware is after initServer() and before listen().

Context

const middleware = async (ctx, next) => { console.log(ctx) await next() console.log(ctx) }

The first printed context shows information such as path, method, request, metadata, and peer, which are obtained before server processing and can be modified. The method includes the type of call, and the metadata and options parameters of the request.

Terminal
{ path: '/helloworld.Greeter/SayGreet', method: { requestStream: false, responseStream: false }, request: { name: 'greeter' }, metadata: Metadata { internalRepr: Map(3) { 'x-client-hostname' => [Array], 'x-service-path' => [Array], 'user-agent' => [Array] }, options: {} }, peer: '127.0.0.1:64929' }

The second printed context, in addition to response, is added after execution and can be modified. The final response is based on the modified result.

Terminal
{ path: '/helloworld.Greeter/SayGreet', method: { requestStream: false, responseStream: false }, request: { name: 'greeter' }, metadata: Metadata { internalRepr: Map(3) { 'x-client-hostname' => [Array], 'x-service-path' => [Array], 'user-agent' => [Array] }, options: {} }, peer: '127.0.0.1:64929', response: { message: 'hello greeter by Greeter', count: 1 } }

Example

log.js
const log = async (ctx, next) => { const startTime = Date.now() await next() const responseTimeMs = Date.now() - startTime console.log({ path: ctx.path, peer: ctx.peer, request: ctx.request, responseTimeMs }) } export default log

Add middleware:

server.js
import log from './log.js' // other... server.use(log)

With this, we have completed the writing and usage of server-side middleware.

Last updated on