Skip to content

NestJS integration module #268

Closed

Description

@adenhertog

NestJS is the most common way enterprise TypeScript teams structure services, and a natural way in to node-ts/bus.

  • BusModule.forRoot / forRootAsync.
  • Handlers and workflows as providers, discovered through decorators.
  • A container adapter backed by Nest's DI.
  • Graceful shutdown tied to Nest lifecycle hooks.

Related DI integration issues: #180.

Activity

  1. adenhertog commented on Oct 7, 2026

    @adenhertog
    ContributorAuthor

    Accepted design (maintainer, 2026-10-07)

    • Package: @node-ts/bus-nestjs in packages/bus-nestjs. Peers @nestjs/common and @nestjs/core ^11.0.0 || ^12.0.0 (only APIs both have), @node-ts/bus-core ^2.0.0. CI tests Nest 12.
    • Configuration: BusModule.forRoot({ configure }) and forRootAsync({ imports, inject, useFactory }) take a factory that returns an unbuilt BusConfiguration, so every builder method works and nothing is duplicated. The module adds the Nest container adapter, the discovered handlers and workflows, withInterruptSignals([]) and Nest's Logger (unless withLogger() is used), then builds.
    • Registration: BusModule.forFeature({ handlers, workflows }) and forFeatureAsync({ inject, useFactory }) register class and function handlers and workflows (function handlers get dependencies through inject). @BusHandler() and @BusWorkflow(), made with DiscoveryService.createDecorator(), are optional sugar for classes. No decorators on messages.
    • Injection and several buses: the default bus is injected as BusInstance. Named buses use forRoot({ name }), getBusToken(name) and @InjectBus(name), with forFeature({ bus }) and @BusHandler({ bus }). Each bus has its own container adapter.
    • Lifecycle: built in the provider factory; initialize() and start() in onApplicationBootstrap (start() skipped for buses that don't poll, through a small public getter in bus-core); stop() in onModuleDestroy; dispose() in onApplicationShutdown. lifecycle: 'manual' leaves starting to the app. Graceful SIGTERM needs app.enableShutdownHooks().
    • Provisioning: bus provision also accepts a module that exports a built, uninitialized BusInstance (or a function returning one), and bus.mjs exits explicitly. createBusForProvisioning(AppModule, { bus? }) boots the app with the bus' lifecycle off and returns the bus.
    • Scopes: singleton providers are resolved with moduleRef.get. Request-scoped and transient ones are resolved with one ContextId per received message, with { message, attributes } registered as REQUEST. Bootstrap fails with a named error if a class handler or workflow isn't a provider.
    • Tests: Nest 12 is ESM-only, so Jest runs with NODE_OPTIONS=--experimental-vm-modules (falling back to transformIgnorePatterns if an existing suite breaks). Tests use Test.createTestingModule with InMemoryQueue and the existing handlerContext() fakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestroadmapPart of the project roadmap

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions