Library / SDK
nestjs/terminus avatar
nestjs/terminus

Terminus: health checks that speak Nest's dependency injection

Terminus module for Nest framework (node.js) :robot:

745 stars112 forksTypeScriptMIT

At a glance

What is it?
The @nestjs/terminus module adds readiness and liveness endpoints to a Nest application by wrapping each dependency check in a health indicator and resolving them through the framework's own injection, so a database ping looks like any other injected service. It covers a wide set of backends, from TypeORM and Prisma to Kafka, Redis and gRPC, but it is a health check layer and not a monitoring system, and its readiness and liveness endpoints are deliberately not the same thing.
Who is it for?
Adopt Terminus if you run Nest and need a health endpoint that an orchestrator or load balancer can poll, because each dependency is checked through an indicator resolved by Nest's injector, so a check for a database, a queue or a cache is configured in the same place as the service itself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Health checks that are ordinary injected providers

Terminus is a Nest module, and the module description in the package manifest is more precise than the repository description: it provides readiness and liveness health checks for NestJS. The repository itself says it contains integrated healthchecks for Nest.

The mechanism is the interesting part. A health check is not a bespoke monitoring agent; it is a set of health indicators, one per dependency, resolved through Nest's dependency injection container. That means a database check is configured in the same module context as the database itself, and a check that cannot resolve its dependency fails at startup rather than at the moment someone polls the health endpoint.

That design has a second effect. Because the indicators are ordinary providers, the set of technologies the module can check is not a fixed list built into Terminus, it is the set of indicators the package ships, and the package keywords name them: typeorm, microorm, mongoose, sequelize, prisma, grpc, kafka, rabbitmq, nats, redis, amqp, amqplib and axios.

The audience is a team running Nest in an environment where something else asks whether the service is up, whether it is ready to take traffic, or whether it should be replaced. Kubernetes probes are the obvious case, and the distinction between the two kinds of check is the concept the whole module is built around.

Installing the module and making the dependency available

Installation is a single npm command:

bash
npm install --save @nestjs/terminus

The next step is the one that trips people up, and the documentation spells it out: you have to make sure the additionally needed modules are available in the module context, for example TypeOrmModule if you intend to do database health checks. The health indicator and the database connection are separate providers, and the indicator needs the database module imported alongside it:

typescript
// app.module.ts

@Module({
  controllers: [HealthController],
  imports:[
    // Make sure TypeOrmModule is available in the module context
    TypeOrmModule.forRoot({ ... }),
    TerminusModule
  ],
})
export class HealthModule { }

Note the import order in that snippet, with the database module registered before TerminusModule. That is not decoration, it is the dependency graph, and getting it wrong shows up as an unresolved provider at bootstrap rather than as a failing health check later.

The second prerequisite is the version of Node. The package declares the engine constraint ^20.19.0, ^22.12.0 or >=24.0.0, which is stricter than the Node 18 era of the original releases and is the kind of requirement that should be checked before you start, not during the install.

The controller, the HealthCheck decorator and the result document

A health endpoint is an ordinary controller with one extra decorator. The HealthCheckService is injected, the indicator is injected, and the check method returns a promise of a result document:

typescript
@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private db: TypeOrmHealthIndicator,
  ) {}
  @Get()
  @HealthCheck()
  readiness() {
    return this.health.check([
      async () => this.db.pingCheck('database').withTimeout(1000),
    ]);
  }
}

Three details carry the design. The @HealthCheck() decorator is what marks the route as a health check rather than an ordinary GET. The check list is an array of functions rather than an array of results, so each indicator runs lazily and the service can decide how to combine them. And the timeout is set on the individual indicator with withTimeout(1000), which is how a slow database is turned into a failed check rather than a hanging request.

The response document has a fixed shape:

json
{
  "status": "ok",
  "info": {
    "database": {
      "status": "up"
    }
  },
  "details": {
    "database": {
      "status": "up"
    }
  }
}

The information and details sections both carry the per-indicator status, and the top level status is the aggregate. With one check, the document is small. With ten, the details section is where the per-dependency breakdown lives, and that is the part a human reads when a probe fails.

Readiness and liveness are different questions about the same app

The package description names two kinds of check, readiness and liveness, and the distinction is the single most important thing to understand before you point a probe at a Terminus endpoint.

Liveness answers whether the process should be restarted. It should be cheap and it should not depend on anything outside the container, because a liveness probe that fails when a database is down will restart every replica of a healthy application and turn a partial outage into a full one. Readiness answers whether the instance should receive traffic. It is allowed to check the database, because a replica that cannot reach its database should be taken out of rotation without being killed.

Terminus gives you the machinery for both, but it does not decide for you which checks belong where. In the example above, the method is named readiness, and it includes a database ping with a one second timeout, which is the readiness pattern. A liveness route in the same application would return a health check with no indicators, or with indicators that only test the process itself.

The failure mode when this is got wrong is specific and worth naming. A single endpoint used for both, with a database check in it, means a database outage triggers restarts, and the restarts do not fix the database. The application comes back with a fresh process, immediately fails the same check, and is killed again, while the database is still down. The health endpoint will faithfully report the truth throughout, which makes it easy to misdiagnose as an application problem.

The indicator catalogue and what each one actually probes

The sample applications in the repository are the best catalogue of what the module can check, because each one is a minimal application wired to a single dependency. There is a dogs app, a mongoose app, a microservice app, a memory app, a grpc app, a typeorm app, a multi database typeorm app, a sequelize app, a multi database mongoose app, an http app, a disk app, a mirkoorm app and a prisma app.

The set covers several distinct kinds of dependency, and the categories behave differently.

Databases are the most common: TypeORM, Mongoose, Sequelize, Prisma and MicroORM, plus the multi database variants that show the indicator accepting more than one connection. Queues and brokers are covered by the Kafka, RabbitMQ, NATS and AMQP indicators, which check the broker rather than a consumer. Caches are covered by Redis, and remote calls by the HTTP indicator, with axios listed among the keywords. gRPC is separate again, since it is a remote call over a specific protocol, and the disk indicator checks a filesystem path, which is the only local resource in the set.

The http and disk indicators are the two that behave least like the others, because a remote HTTP call or a local disk check can fail for reasons that have nothing to do with whether the service can serve traffic. A disk health check that fails because a mounted volume is temporarily read-only is a deployment problem, and putting it on the readiness route will drain every replica that has the same mount.

Running the module's own tests, and the Docker stack behind them

Contributing to Terminus means working with the pnpm workflow the package scripts define, and the repository layout shows a monorepo shape with a pnpm-workspace.yaml, a pnpm-lock.yaml and a sample/ directory containing many applications.

The development loop is small:

bash
pnpm i
pnpm dev

To test the library against one of the samples, you go into that sample, build it and start it:

bash
cd sample/000-dogs-app
pnpm build
pnpm start

The note about the sample needing a rebuild and restart once the library is rebuilt is the practical detail that trips up new contributors, since the change is not picked up until both are rebuilt.

Unit tests are a single command:

bash
pnpm test

End to end tests need the Docker Compose stack, and the compose file at the root shows what that means in practice. It defines a MySQL 9 service with a root password and a test database, a MongoDB service, a RabbitMQ 4.1.4 service with the management image, a Redis service, and a Kafka stack built from a Confluent ZooKeeper 7.9.10 and a Kafka broker 7.9.9. Every one of them is mapped to a host port, the MySQL one with its own health check using mysqladmin ping.

That compose file is the clearest statement of the module's ambitions. The e2e suite runs against real brokers and real databases rather than mocks, which is the right way to test health indicators, because a ping indicator that only works against a stub is testing the stub.

Licence, release cadence and what this module is not

Terminus is MIT licensed, with the licence file at the root, and it is part of the Nest project family, whose support model the documentation describes as depending on sponsors and backers, with a link to the Nest support page. The authors are Kamil Mysliwiec and Livio Brunner, and the package manifest lists Livio Brunner as the author.

The release history shows a large major version, 12.0.0 on 2026-08-31, a 12.0.0-next.1 prerelease published the same day, and 12.1.0 on 2026-09-20. The last push to the repository was on 2026-10-19 and the repository is not archived, so the work is current.

A major bump every so often is worth pausing on for anyone who has an existing integration. The package is published as an ES module, with the type field set to module and exports pointing at the built dist directory, so a CommonJS application consuming it needs to be aware of that. The engines constraint rules out older Node versions entirely, and the 12.x line also means the public API of the indicator constructors and the result document can move between majors.

And the boundary of what it does. A health endpoint returns a status document at the moment it is called. It does not keep history, it does not aggregate, it does not notify anyone, and it does not tell you whether the service was slow five minutes ago. If you need trends, you need a monitoring system that scrapes the endpoint. If you need to know which dependency failed at 3am, the health endpoint can tell you at 3am, but only if something recorded it.

Editorial conclusion

Adopt Terminus if you run Nest and need a health endpoint that an orchestrator or load balancer can poll, because each dependency is checked through an indicator resolved by Nest's injector, so a check for a database, a queue or a cache is configured in the same place as the service itself. Do not adopt it expecting metrics, tracing or alerting, since it returns a status document and nothing more, and do not point a liveness probe at an endpoint that includes your database check, because a database blip would then restart every pod rather than just removing one from rotation. Before you wire it into a deployment, confirm the Node version constraint, since the package declares ^20.19.0, ^22.12.0 or >=24.0.0, and decide which checks belong on the readiness route and which on liveness, since the module does not make that distinction for you.

Frequently asked questions

How do I install and set up health checks in @nestjs/terminus?

Install it with npm install --save @nestjs/terminus, then import TerminusModule alongside the module for the dependency you want to check, for example TypeOrmModule.forRoot, in the same Nest module. Then write a controller with a @Get() route and the @HealthCheck() decorator that returns this.health.check([...]).

What is the difference between a readiness and a liveness health check?

The package provides both. Liveness answers whether the process should be restarted and should not depend on external services, while readiness answers whether the instance should receive traffic and may check the database. The module gives you the machinery for each, but the choice of which checks go on which route is left to you.

Which databases and brokers can Terminus check?

The package keywords list typeorm, microorm, mongoose, sequelize, prisma, grpc, kafka, rabbitmq, nats, redis, amqp, amqplib and axios, and the sample applications cover TypeORM, Mongoose, Sequelize, Prisma, multi database setups, gRPC, HTTP, disk and memory.

How do I set a timeout on a health check in Terminus?

The timeout is applied to the individual indicator through the withTimeout method, as in this.db.pingCheck('database').withTimeout(1000) in the documented controller example, so a slow dependency produces a failed check rather than a hanging request.

What does the Terminus health check response look like?

A JSON document with a top level status of ok, an info section and a details section, each carrying a status per indicator. With a single database check the document shows status ok with the database entry reported as up in both sections.

Official sources

  1. License: MIT
  2. nestjs/terminus on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nestjs-terminus.svg)](https://hysenlabs.com/projects/nestjs-terminus)