Express Service Readiness Middleware

raw JSON →
1.0.25 verified Thu Apr 23 auth: no javascript

This module provides Express.js middleware for determining whether server routes should be exposed based on the health status of critical service dependencies. Currently stable at version 1.0.25, the package is typically updated for bug fixes and compatibility with new Express.js versions. A key differentiator is its focus on *readiness* at startup rather than continuous *liveness* monitoring. Once the service achieves a 'ready' state, it maintains this status for its lifetime, meaning subsequent failures of critical dependencies will *not* revert it to an 'unready' state and block non-whitelisted routes. It returns a 502 status code for non-whitelisted routes when critical dependencies are not ready, and allows specific paths to be whitelisted for access even during unready periods. It distinguishes between critical and non-critical dependencies, allowing flexible health definitions.

error TypeError: (0 , express_service_readiness_middleware_1.createReadinessMiddleware) is not a function
cause This error typically occurs when attempting to `require()` an ESM-first package in a CommonJS context, or using incorrect import syntax for named exports. While the package ships types, the example in the README uses CommonJS `require()` which might conflict with an ESM-configured project (e.g., `"type": "module"` in `package.json`).
fix
If your project is configured for ESM, use import { createReadinessMiddleware } from 'express-service-readiness-middleware';. If strictly using CommonJS and facing this issue, ensure your tsconfig.json (if applicable) and build setup correctly target CommonJS modules, or explicitly set "type": "commonjs" in your package.json.
error TypeError: dependency.isReady is not a function
cause A dependency object provided to `createReadinessMiddleware` or `checkDependenciesHealth` is missing the `isReady` property, or its value is not a function that returns a Promise.
fix
Ensure all objects in the dependencies array have an isReady property set to an asynchronous function that returns a Promise<boolean>, e.g., isReady: () => Promise.resolve(true).
error Service is not becoming ready (502 errors persist)
cause The critical dependencies' `isReady` functions are consistently returning `false` or taking longer than `maximumWaitTimeForServiceReadinessInMilliseconds` to resolve to `true`, preventing the service from transitioning to a 'ready' state.
fix
Inspect the isReady implementations for your critical dependencies. Check external services, database connections, or other resources. Consider temporarily increasing maximumWaitTimeForServiceReadinessInMilliseconds in the middleware configuration for debugging, and ensure your setLogger is configured to view readiness logs.
gotcha This middleware implements a 'readiness' check, not a continuous 'liveness' check. Once the service's critical dependencies are initially deemed ready, the service will remain 'ready' for its lifetime. Subsequent failures of critical dependencies will NOT revert the service to an 'unready' state or block non-whitelisted routes, as it would with a liveness probe. This design choice may differ from expectations for ongoing health monitoring.
fix If continuous health monitoring (liveness) is required, implement a separate liveness endpoint using `checkDependenciesHealth` that responds dynamically to current dependency health.
gotcha No informational logging will occur internally unless a logger is explicitly set using the `setLogger` function. This can make debugging readiness issues challenging if logging is not configured.
fix Call `setLogger(console)` or provide a compatible logger object (one with a `log` method) early in your application's bootstrap process.
gotcha The `isReady` and `isHealthy` functions provided in dependency objects must return Promises. If these promises reject due to an error, they are treated as the dependency *not* being ready/healthy. Ensure robust error handling within these promise-returning functions to accurately reflect dependency status.
fix Implement `isReady` and `isHealthy` functions to always return a Promise, handling any potential errors by resolving to `false` or ensuring the promise rejection is caught and processed gracefully within the middleware's logic.
npm install express-service-readiness-middleware
yarn add express-service-readiness-middleware
pnpm add express-service-readiness-middleware

Demonstrates setting up an Express application with readiness middleware. It includes critical and non-critical dependencies, whitelisted paths, and endpoints for both liveness and readiness, highlighting the middleware's gating behavior during startup.

import express from 'express';
import { createReadinessMiddleware, setLogger, checkDependenciesHealth } from 'express-service-readiness-middleware';

const app = express();
const PORT = process.env.PORT || 3000;

// Set a logger for internal messages (optional, but recommended)
setLogger(console);

// Simulate a critical database dependency
let isDatabaseReady = false;
setTimeout(() => {
  console.log('Database became ready after 5 seconds.');
  isDatabaseReady = true;
}, 5000);

// Simulate a non-critical cache dependency that might fail initially
let isCacheHealthy = true;
const toggleCacheHealth = () => {
  isCacheHealthy = !isCacheHealthy;
  console.log(`Cache is now ${isCacheHealthy ? 'healthy' : 'unhealthy'}.`);
};
setInterval(toggleCacheHealth, 15000);

const dependencies = [
  {
    name: 'database',
    critical: true,
    isReady: () => Promise.resolve(isDatabaseReady),
    isHealthy: () => Promise.resolve(isDatabaseReady) // For liveness, same as readiness here
  },
  {
    name: 'cache',
    critical: false,
    isReady: () => Promise.resolve(true), // Cache doesn't block readiness
    isHealthy: () => Promise.resolve(isCacheHealthy) // Its health can fluctuate
  }
];

// Register the readiness middleware before other routes
// Requests to non-whitelisted paths will get 502 until 'database' is ready.
app.use(createReadinessMiddleware(dependencies, { 
  whitelistedPaths: ['/liveness', '/ready'] 
}));

// Liveness endpoint (always accessible)
app.get('/liveness', (req, res) => {
  res.status(200).send('Service is live');
});

// Readiness endpoint (checks current readiness status dynamically)
app.get('/ready', async (req, res) => {
  const health = await checkDependenciesHealth(dependencies);
  if (health.allCriticalDependenciesHealthy) {
    res.status(200).json({ status: 'ready', details: health });
  } else {
    res.status(503).json({ status: 'not ready', details: health });
  }
});

// Application routes (will be gated by readiness middleware)
app.get('/', (req, res) => {
  res.send('Hello from the ready service!');
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
  console.log('Try accessing / and /liveness immediately, then / after 5 seconds.');
});