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 aUsage
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.
{
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.
{
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
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 logAdd middleware:
import log from './log.js'
// other...
server.use(log)With this, we have completed the writing and usage of server-side middleware.