nice-grpc Client Retry Middleware

3.1.14 · active · verified Tue Apr 21

nice-grpc-client-middleware-retry is a client-side middleware for the `nice-grpc` TypeScript gRPC library, enabling automatic retries for unary gRPC calls. It integrates seamlessly with the `nice-grpc` client factory to add robust fault tolerance with features like exponential backoff. The current stable version is 3.1.14, and the `nice-grpc` ecosystem, which this package is part of, maintains an active and frequent release cadence, often with updates every few weeks for core packages. A key differentiator is its strong TypeScript support and modern API leveraging Promises and Async Iterables, providing a more ergonomic experience compared to traditional gRPC implementations. It also emphasizes the importance of idempotency, requiring explicit configuration for retries on non-idempotent operations to prevent unintended side effects.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to apply the `retryMiddleware` to a `nice-grpc` client, configure retry parameters like max attempts and retryable statuses, and handle potential errors. It also illustrates the `onRetry` callback for logging retry attempts and highlights the configuration options for exponential backoff.

import { createChannel, createClientFactory, ClientError, Status } from 'nice-grpc';
import { retryMiddleware } from 'nice-grpc-client-middleware-retry';

// Imagine you have a generated Protobuf service definition (e.g., from ts-proto)
// interface ExampleService extends Client<typeof ExampleServiceDefinition> {}
// const ExampleServiceDefinition = { ... }; // Your service definition

// Mock a service definition for demonstration
const ExampleServiceDefinition = {
  name: 'ExampleService',
  methods: {
    exampleMethod: {
      path: '/ExampleService/ExampleMethod',
      requestType: {}, // replace with your actual request type
      responseType: {}, // replace with your actual response type
      options: {},
    },
  },
};

async function runClientWithRetries() {
  const address = process.env.GRPC_SERVER_ADDRESS ?? 'localhost:50051';
  const channel = createChannel(address);

  const clientFactory = createClientFactory().use(retryMiddleware);

  const client = clientFactory.create(ExampleServiceDefinition, channel);

  let attempt = 0;
  try {
    console.log('Attempting to call exampleMethod with retries...');
    const response = await client.exampleMethod({}, {
      // Enable retry if method is not marked as idempotent in Protobuf options
      retry: true, 
      retryMaxAttempts: 3, 
      retryableStatuses: [Status.UNAVAILABLE, Status.INTERNAL, Status.UNKNOWN],
      onRetry(error: ClientError, attemptNumber: number) {
        console.warn(`Retry attempt ${attemptNumber} due to ${Status[error.code]}: ${error.details}`);
      },
      // Optional: Exponential backoff configuration
      retryDelay: 100, // initial delay in ms
      retryDelayMax: 1000, // max delay in ms
      retryDelayMultiplier: 2, // multiplier for each successive delay
    });
    console.log('Successfully received response:', response);
  } catch (error) {
    if (error instanceof ClientError) {
      console.error(`Client error after all retries: ${Status[error.code]} - ${error.details}`);
    } else {
      console.error('An unexpected error occurred:', error);
    }
  } finally {
    channel.close();
  }
}

runClientWithRetries();

view raw JSON →