Registry / http-networking / nice-grpc-server-middleware-terminator

nice-grpc-server-middleware-terminator

JSON →
library2.0.17jsnpmunverified

The `nice-grpc-server-middleware-terminator` package provides a crucial server middleware for `nice-grpc` to facilitate graceful shutdown of gRPC services by actively terminating long-running calls. It addresses a common challenge in gRPC, particularly with streaming RPCs, where `server.shutdown()` would otherwise block indefinitely until all in-flight requests complete or are cancelled by the client. This middleware injects a `context.signal` into service implementations; when `server.shutdown()` is initiated, this signal is aborted, prompting the service method to stop processing. Clients of terminated calls receive a `UNAVAILABLE: Server shutting down` gRPC error, ensuring that the server can complete its shutdown process within a predictable timeframe. The current stable version is 2.0.17, with updates typically coinciding with the broader `nice-grpc` ecosystem. Its key differentiator is providing a standardized, integrated mechanism within the `nice-grpc` framework for managing server-side RPC lifecycle during shutdown, directly supporting the modern `AbortSignal` pattern.

npm install nice-grpc-server-middleware-terminator
INSTALL
IMPORT
SIG · NICE-GRPC-SERVER-M
N
nice-grpc-server-middleware-terminator
http-networkingjavascriptv2.0.17
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

createTerminatorMiddleware
import { createTerminatorMiddleware } from 'nice-grpc-server-middleware-terminator';
const createTerminatorMiddleware = require('nice-grpc-server-middleware-terminator');
The `nice-grpc` ecosystem, and by extension its middleware, is primarily designed for ES Modules. CommonJS `require()` is not supported.
TerminatorContext
import type { TerminatorContext } from 'nice-grpc-server-middleware-terminator';
import { TerminatorContext } from 'nice-grpc-server-middleware-terminator';
This is a type definition used to augment `nice-grpc`'s `CallContext`. Use `import type` to avoid bundling unnecessary runtime code.
isTerminated
import { isTerminated } from 'nice-grpc-server-middleware-terminator';
A utility function to check if a `nice-grpc` `ServerError` indicates termination by this middleware.

Demonstrates setting up a `nice-grpc` server with the terminator middleware, initiating a long-running streaming call, and observing the graceful shutdown process which aborts the stream.

import {createServer, ServiceImplementation, ServerError, Status, CallContext} from 'nice-grpc'; import { createTerminatorMiddleware, TerminatorContext } from 'nice-grpc-server-middleware-terminator'; import { AbortController } from 'abort-controller-x'; import * as path from 'path'; import { loadSync } from '@grpc/proto-loader'; import { GrpcObject, PackageDefinition, UntypedServiceImplementation } from '@grpc/grpc-js'; interface ExampleRequest { name: string; } interface ExampleResponse { message: string; } // 1. Define your Protobuf service (example.proto) // syntax = "proto3"; // package example; // service ExampleService { // rpc SayHello (ExampleRequest) returns (ExampleResponse) {} // rpc StreamHello (ExampleRequest) returns (stream ExampleResponse) {} // } const PROTO_PATH = path.resolve(__dirname, 'example.proto'); const packageDefinition: PackageDefinition = loadSync(PROTO_PATH); const grpcObject: GrpcObject = {} as GrpcObject; // Simplified for example, normally loads via @grpc/proto-loader // Simulate the generated service definition const ExampleServiceDefinition = { name: 'example.ExampleService', fullName: 'example.ExampleService', methods: { SayHello: { path: '/example.ExampleService/SayHello', requestStream: false, responseStream: false, requestType: {}, // Simplified responseType: {}, // Simplified responseDeserialize: (b: Buffer) => ({ message: b.toString() }), requestSerialize: (m: ExampleRequest) => Buffer.from(m.name), }, StreamHello: { path: '/example.ExampleService/StreamHello', requestStream: false, responseStream: true, requestType: {}, // Simplified responseType: {}, // Simplified responseDeserialize: (b: Buffer) => ({ message: b.toString() }), requestSerialize: (m: ExampleRequest) => Buffer.from(m.name), } } }; type MyCallContext = CallContext & TerminatorContext; const exampleServiceImpl: ServiceImplementation<typeof ExampleServiceDefinition, MyCallContext> = { async SayHello(request: ExampleRequest, context: MyCallContext): Promise<ExampleResponse> { console.log(`[Server] Received SayHello from ${request.name}`); return { message: `Hello, ${request.name}!` }; }, async *StreamHello(request: ExampleRequest, context: MyCallContext): AsyncIterable<ExampleResponse> { console.log(`[Server] Received StreamHello from ${request.name}. Starting stream...`); let counter = 0; while (true) { try { // IMPORTANT: Check context.signal.aborted regularly in long-running operations // to respond to termination requests. if (context.signal.aborted) { console.log(`[Server] StreamHello for ${request.name} aborted due to server shutdown.`); // This is generally handled by the middleware, but explicit checks are good. throw new ServerError(Status.UNAVAILABLE, 'Server shutting down'); } yield { message: `Stream Hello ${counter++} to ${request.name}` }; await new Promise(resolve => setTimeout(resolve, 500)); // Simulate work } catch (error) { if (error instanceof ServerError && error.code === Status.UNAVAILABLE) { console.log(`[Server] Explicitly caught ServerError for ${request.name}.`); break; // Exit the stream gracefully } console.error(`[Server] Stream error for ${request.name}:`, error); throw error; } } }, }; async function runServer() { const server = createServer() .use(createTerminatorMiddleware()) .add(ExampleServiceDefinition, exampleServiceImpl as UntypedServiceImplementation); const port = 50051; await server.listen(`0.0.0.0:${port}`); console.log(`gRPC server listening on port ${port}`); // Simulate a client making a long-running streaming call // (In a real scenario, this would be a separate client process) async function simulateClient() { const client = createServer().createClient(ExampleServiceDefinition, `0.0.0.0:${port}`); console.log('[Client] Initiating SayHello...'); const unaryResponse = await client.SayHello({ name: 'Alice' }); console.log('[Client] SayHello response:', unaryResponse.message); console.log('[Client] Initiating StreamHello...'); try { for await (const response of client.StreamHello({ name: 'Bob' })) { console.log('[Client] StreamHello response:', response.message); // Simulate client processing for a bit before server shutdown await new Promise(resolve => setTimeout(resolve, 300)); } } catch (error) { if (error instanceof ServerError && error.code === Status.UNAVAILABLE) { console.warn('[Client] Stream terminated by server during shutdown as expected.'); } else { console.error('[Client] StreamHello error:', error); } } } // Start the simulated client call after a short delay setTimeout(simulateClient, 1000); // Simulate graceful shutdown after some time setTimeout(async () => { console.log('\n[Server] Initiating graceful shutdown...'); await server.shutdown(); console.log('[Server] Server shut down gracefully.'); }, 5000); } runServer().catch(console.error);
Debug
Known issues
gotchaThe `nice-grpc-server-middleware-terminator` effectively terminates long-running calls only if the service implementation actively monitors `context.signal.aborted` (or similar AbortSignal patterns) and ceases work. If the service method does not check the signal, the middleware cannot force a stop, and the shutdown will still block.
fix
Ensure all long-running or streaming gRPC service methods frequently check `context.signal.aborted` and handle the termination by throwing a `ServerError` with `Status.UNAVAILABLE` or returning early.
affects: >=1.0.0
breakingWhile this middleware is at v2, its core dependency `nice-grpc` had a significant breaking change with its v3 release, becoming ESM-only. If you upgrade `nice-grpc` to v3, this middleware (and your application) must also be running in an ESM context.
fix
Migrate your project to use ES Modules (e.g., set `"type": "module"` in `package.json` and use `import`/`export` syntax). Ensure your build tools and Node.js environment support ESM.
affects: >=2.0.0 (in context of nice-grpc >=3.0.0)
gotchaWhen a call is terminated by this middleware during server shutdown, the client will receive a gRPC `UNAVAILABLE` status code with the message 'Server shutting down'. This is intended behavior, but might be misinterpreted as an unexpected error by clients if not handled explicitly.
fix
Clients should implement robust error handling for `ServerError` where `error.code === Status.UNAVAILABLE` and `error.details` contains the specific shutdown message, allowing for graceful client-side disconnection or retry logic.
affects: >=1.0.0
Errors
Common errors & fixes
Server shutdown blocked indefinitely (or 'server.shutdown() never resolves')
Long-running gRPC calls (especially streaming ones) are not reacting to the termination signal provided by the middleware.
fix
Modify your service implementation methods to regularly check `context.signal.aborted` and gracefully exit or throw a `ServerError(Status.UNAVAILABLE, 'Server shutting down')` when the signal is aborted.
TypeError: createTerminatorMiddleware is not a function (or 'require is not defined')
Attempting to use ES Module syntax (`import`) in a CommonJS environment, or vice-versa, particularly if `nice-grpc` or its middleware is used in a mixed environment.
fix
Ensure your project is consistently configured for either ES Modules (recommended for `nice-grpc` v3+) or CommonJS. For ESM, set `"type": "module"` in `package.json` and use `import` statements. For CommonJS, you might need to find a CommonJS compatible version or transpilation if available, but `nice-grpc` ecosystem strongly favors ESM.
Upgrade
Version history
2.0.17latest on npm
Audit
Dependencies
nice-grpcrequiredCore gRPC server library that this package extends with termination middleware.
nice-grpc-commonrequiredProvides common types and utilities, including `CallContext` and `ServerError`, used by `nice-grpc` middleware.
abort-controller-xrequiredProvides cross-platform AbortController implementation used for signal propagation.
Agent activity
10 hits · last 30 days
node
8
OpenAI (training)
2
Resources
nice-grpc-server-middleware-terminator — npm install nice-grpc-server-middleware-terminator · libregistry