npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

nestjs-saga

v0.3.0

Published

Implementation of saga pattern for NestJS

Downloads

825

Readme

nestjs-saga

Basic implementation of saga pattern for NestJS (do not confuse it with the built-in sagas).

This module is not too much related to microservices sagas but could be used as a base to implement it.

Highly inspired by node-sagas but rewritten a bit for more convenient usage with NestJS.

installation

npm i nestjs-saga @nestjs/cqrs

usage

define

import { Builder, Saga } from 'nestjs-saga';

class FooSagaCommand {
  constructor(
    public bar: string,
    public baz: number,
  ) {}
}

class FooSagaResult {
  // ...
}

@Saga(FooSagaCommand)
class FooSaga {
  // Define `saga` field using Builder<FooSagaCommand> or if you want to return
  // some value use: Builder<FooSagaCommand, FooSagaResult>
  saga = new Builder<FooSagaCommand>()

    // Add a step with the name, invocation and compensation functions
    .step('do something')
    .invoke(this.step1)
    .withCompensation(this.step1Compensation)

    // Add another one, name and compensation could be omitted
    .step()
    .invoke(this.step2)

    // If builder with result type is used (Builder<Command, Result>) then it's
    // required to add last `return` step, final `build` step will be available
    // only after this one. If no result type provided in Builder then this
    // method won't be available in types and saga will return `undefined`
    .return(this.buildResult)

    // After all steps `build` should be called
    .build();

  // Each invocation and compensation methods are called with the command as an
  // argument
  step1(cmd: FooSagaCommand) {

    // Each time saga is called as a new instance, so it's safe to save it's
    // state in own fields
    this.step1Result = 42;
  }

  // If step throws error then compensation chain is started in a reverse order:
  // step1 -> step2 -> step3(X) -> compensation2 -> compensation1
  step2(cmd: FooSagaCommand) {
    if (this.step1Result != 42) throw new Error('oh no!');
  }

  // After all compensations are done `SagaInvocationError` is thrown. It will
  // wrap original error which can be accessed by `originalError` field
  step1Compensation(cmd: FooSagaCommand) {

    // If one of compensations throws error then compensations chain is stopped
    // and `SagaCompensationError` is thrown. It will wrap original error which
    // can be accessed by `originalError` field
    if (this.step1Result != 42) throw new Error('oh no!');
  }

  // If saga should return some result pass it's type to the Builder generic and
  // use `return` method in the build chain with a callback that returns this
  // class or type
  buildResult(cmd: FooSagaCommand): Result | Promise<Result> {
    return new Result();
  }
}

register

import { CqrsModule } from '@nestjs/cqrs';
import { SagaModule } from 'nestjs-saga';

@Module({
  imports: [
    CqrsModule,
    SagaModule.register({
      imports: [...], // optional
      providers: [...], // optional
      sagas: [FooSaga, BarSaga, BazSaga], // required
    }),
  ],
})
class AppModule {}

run

import { CommandBus } from '@nestjs/cqrs';
import { SagaInvocationError, SagaCompensationError } from 'nestjs-saga';

class AnyServiceOrController {
  constructor(private commandBus: CommandBus) {}

  someMethod() {
    try {
      // If saga defined with the result type, then result will be passed,
      // otherwise it's `undefined`
      const result = await this.commandBus.execute(new FooSagaCommand(...args));

    } catch (e) {
      if (e instanceof SagaInvocationError) {
        // Saga failed but all compensations succeeded.
        e.originalError // could be used to get access to original error
        e.step // can be used to understand which step failed

      } else if (e instanceof SagaCompensationError) {
        // Saga failed and one of compensations failed.
        e.originalError // could be used to get access to original error
        e.step // can be used to understand which step compensation failed
      }
    }
  }
}