{"id":13818201,"url":"https://github.com/juicycleff/nestjs-event-store","last_synced_at":"2025-04-06T19:11:13.518Z","repository":{"id":35447239,"uuid":"217759142","full_name":"juicycleff/nestjs-event-store","owner":"juicycleff","description":"NestJS CQRS module for EventStore.org. It requires @nestjs/cqrs","archived":false,"fork":false,"pushed_at":"2022-10-24T11:54:14.000Z","size":563,"stargazers_count":198,"open_issues_count":26,"forks_count":28,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-04-14T09:58:57.541Z","etag":null,"topics":["cqrs","cqrs-pattern","event-sourcing","event-storage","event-store","kafka","kafka-streams","library","nats","nats-streaming","nest","nestjs"],"latest_commit_sha":null,"homepage":"https://xraph.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juicycleff.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-26T19:28:45.000Z","updated_at":"2024-04-08T07:05:10.000Z","dependencies_parsed_at":"2023-01-15T21:26:21.047Z","dependency_job_id":null,"html_url":"https://github.com/juicycleff/nestjs-event-store","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juicycleff%2Fnestjs-event-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juicycleff%2Fnestjs-event-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juicycleff%2Fnestjs-event-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juicycleff%2Fnestjs-event-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juicycleff","download_url":"https://codeload.github.com/juicycleff/nestjs-event-store/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535516,"owners_count":20954576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cqrs","cqrs-pattern","event-sourcing","event-storage","event-store","kafka","kafka-streams","library","nats","nats-streaming","nest","nestjs"],"created_at":"2024-08-04T07:00:35.703Z","updated_at":"2025-04-06T19:11:13.497Z","avatar_url":"https://github.com/juicycleff.png","language":"TypeScript","funding_links":[],"categories":["Integrations","TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\nNestJs Event Store\n\u003c/h1\u003e\n  \n\u003cp align=\"center\"\u003e\n  NestJS CQRS module with support EventStore.org and NATS Streaming. It requires @nestjs/cqrs.\n\u003c/p\u003e\n    \u003cp align=\"center\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://www.npmjs.com/package/@juicycleff/nestjs-event-store\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@juicycleff/nestjs-event-store?style=flat-square\" alt=\"NPM Version\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://img.shields.io/npm/l/@juicycleff/nestjs-event-store?style=flat-square\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/@juicycleff/nestjs-event-store?style=flat-square\" alt=\"License\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://img.shields.io/github/languages/code-size/juicycleff/nestjs-event-store?style=flat-square\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/github/languages/code-size/juicycleff/nestjs-event-store?style=flat-square\" alt=\"Code Size\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://img.shields.io/github/languages/top/juicycleff/nestjs-event-store?style=flat-square\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/github/languages/top/juicycleff/nestjs-event-store?style=flat-square\" alt=\"Top Language\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://img.shields.io/codacy/grade/0944a2f07aca403da4d4637606af7478?style=flat-square\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/codacy/grade/dc460840375d4ac995f5647a5ed10179?style=flat-square\" alt=\"Top Language\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Installation\n\n```bash\n$ yarn add @juicycleff/nestjs-event-store\n$ yarn add node-nats-streaming node-eventstore-client\n```\n\n## Description\nThis module aims to bridge the gap between NestJs and popular event store brokers like [Event Store](https://eventstore.org) and [NATS Streaming](https://nats.io) with support for kafka coming.\n\n#### [Event Store](https://eventstore.org)\nIt supports all different subscription strategies in EventStore.Org,\nsuch as Volatile, CatchUp and Persistent subscriptions fairly easily. There is support for a storage adapter interface for storing catchup events type last checkpoint position, so\nthe checkpoint can be read on start up; The adapter interface is very slim and easy and can be assigned preferably using the `EventStoreModule.registerFeatureAsync` method.\nAdapter data store examples coming soon.\n\nNote: if your event broker type is Event Store then featureStreamName should look like `'$ce-user'`, then you should name your domain argument should be `user` without `$ce`, for example.\n\n```typescript\nexport class UserCreatedEvent implements IEvent {\n    constructor(\n        public readonly user: any // This what im talking about.\n    )  { }\n}\n```\nThe way this works is we group the event based the first argument in the constructor name and this argument name must be a substring of featureStreamName. I'm sorry you can't pass you your own unique name at the moment, but I will add support for it\n\n#### [NATS Streaming](https://nats.io)\nIt supports all both durable/persistent subscription with shared subscription and volatile. It does not have the limitations of [Event Store](https://eventstore.org) stated above.\n\nNote: if your event broker type is NATS then featureStreamName  should look like `'user'`.\n\n### Setup from versions from `v3.1.15`\n#### Setup NATS\n##### Setup root app module for NATS\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\n\n@Module({\n  imports: [\n    EventStoreModule.register({\n      type: 'nats',\n      groupId: 'groupId',\n      clusterId: 'clusterId',\n      clientId: 'clientId', // Optional (Auto generated with uuid)\n      options: {\n        url: 'nats://localhost:4222',\n        reconnect: true,\n        maxReconnectAttempts: -1,\n      },\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n##### Setup async root app module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\nimport { EventStoreConfigService } from './eventstore-config.service';\n\n@Module({\n  imports: [\n    EventStoreModule.registerAsync({\n      type: 'nats',\n      useClass: EventStoreConfigService\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n##### Setup feature module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { UsersController } from './users.controller';\nimport { AccountEventHandlers, UserLoggedInEvent } from '@ultimatebackend/core';\nimport { AccountSagas } from '../common';\nimport { EventStoreModule, EventStoreSubscriptionType } from '@juicycleff/nestjs-event-store';\n\n@Module({\n  imports: [\n    EventStoreModule.registerFeature({\n      featureStreamName: 'user',\n      type: 'nats',\n      subscriptions: [\n        {\n          type: EventStoreSubscriptionType.Persistent,\n          stream: 'account',\n          durableName: 'svc-user',\n        }\n      ],\n      eventHandlers: {\n        UserLoggedInEvent: (data) =\u003e new UserLoggedInEvent(data),\n      },\n    })\n  ],\n  providers: [...AccountEventHandlers, AccountSagas],\n  controllers: [UsersController],\n})\nexport class UsersModule {}\n```\n\n#### Setup EventStore\n##### Setup root app module for EventStore\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\n\n@Module({\n  imports: [\n    EventStoreModule.register({\n      type: 'event-store',\n      tcpEndpoint: {\n        host: 'localhost',\n        port: 1113,\n      },\n      options: {\n        maxRetries: 1000, // Optional\n        maxReconnections: 1000,  // Optional\n        reconnectionDelay: 1000,  // Optional\n        heartbeatInterval: 1000,  // Optional\n        heartbeatTimeout: 1000,  // Optional\n        defaultUserCredentials: {\n          password: 'admin',\n          username: 'chnageit',\n        },\n      },\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n##### Setup async root app module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\nimport { EventStoreConfigService } from './eventstore-config.service';\n\n@Module({\n  imports: [\n    EventStoreModule.registerAsync({\n      type: 'event-store',\n      useClass: EventStoreConfigService\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n##### Setup feature module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { CommandBus, CqrsModule, EventBus } from '@nestjs/cqrs';\nimport { EventStoreModule, EventStore, EventStoreSubscriptionType } from '@juicycleff/nestjs-event-store';\n\nimport {\n  UserCommandHandlers,\n  UserLoggedInEvent,\n  UserEventHandlers,\n  UserQueryHandlers,\n} from '../cqrs';\nimport { UserSagas } from './sagas';\nimport { MongoStore } from './mongo-eventstore-adapter';\n\n@Module({\n  imports: [\n    CqrsModule,\n    EventStoreModule.registerFeature({\n      featureStreamName: '$ce-user',\n      type: 'event-store',\n      store: MongoStore, // Optional mongo store for persisting catchup events position for microservices to mitigate failures. Must implement IAdapterStore\n      subscriptions: [\n        {\n          type: EventStoreSubscriptionType.CatchUp,\n          stream: '$ce-user',\n          resolveLinkTos: true, // Default is true (Optional)\n          lastCheckpoint: 13, // Default is 0 (Optional)\n        },\n      ],\n      eventHandlers: {\n        UserLoggedInEvent: (data) =\u003e new UserLoggedInEvent(data),\n      },\n    }),\n  ],\n  \n  providers: [\n    UserSagas,\n    ...UserQueryHandlers,\n    ...UserCommandHandlers,\n    ...UserEventHandlers,\n  ],\n})\nexport class UserModule {}\n```\n\n### Setup from versions from `v3.0.0 to 3.0.5`\n##### Setup root app module\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\n\n@Module({\n  imports: [\n    EventStoreModule.register({\n      tcpEndpoint: {\n        host: process.env.ES_TCP_HOSTNAME || AppConfig.eventstore?.hostname,\n        port: parseInt(process.env.ES_TCP_PORT, 10) || AppConfig.eventstore?.tcpPort,\n      },\n      options: {\n        maxRetries: 1000, // Optional\n        maxReconnections: 1000,  // Optional\n        reconnectionDelay: 1000,  // Optional\n        heartbeatInterval: 1000,  // Optional\n        heartbeatTimeout: 1000,  // Optional\n        defaultUserCredentials: {\n          password: AppConfig.eventstore?.tcpPassword,\n          username: AppConfig.eventstore?.tcpUsername,\n        },\n      },\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n##### Setup async root app module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\nimport { EventStoreConfigService } from './eventstore-config.service';\n\n@Module({\n  imports: [\n    EventStoreModule.registerAsync({\n      useClass: EventStoreConfigService\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n## Setup module\n*Note* `featureStreamName` field is not important if you're subscription type is persistent'\n\n##### Setup feature module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { CommandBus, CqrsModule, EventBus } from '@nestjs/cqrs';\nimport { EventStoreModule, EventStore, EventStoreSubscriptionType } from '@juicycleff/nestjs-event-store';\n\nimport {\n  UserCommandHandlers,\n  UserCreatedEvent,\n  UserEventHandlers,\n  UserQueryHandlers,\n} from '../cqrs';\nimport { UserSagas } from './sagas';\nimport { MongoStore } from './mongo-eventstore-adapter';\n\n@Module({\n  imports: [\n    CqrsModule,\n    EventStoreModule.registerFeature({\n      featureStreamName: '$ce-user',\n      store: MongoStore, // Optional mongo store for persisting catchup events position for microservices to mitigate failures. Must implement IAdapterStore\n      subscriptions: [\n        {\n          type: EventStoreSubscriptionType.CatchUp,\n          stream: '$ce-user',\n          resolveLinkTos: true, // Default is true (Optional)\n          lastCheckpoint: 13, // Default is 0 (Optional)\n        },\n        {\n          type: EventStoreSubscriptionType.Volatile,\n          stream: '$ce-user',\n        },\n        {\n          type: EventStoreSubscriptionType.Persistent,\n          stream: '$ce-user',\n          persistentSubscriptionName: 'steamName',\n          resolveLinkTos: true,  // Default is true (Optional)\n        },\n      ],\n      eventHandlers: {\n        UserLoggedInEvent: (data) =\u003e new UserLoggedInEvent(data),\n        UserRegisteredEvent: (data) =\u003e new UserRegisteredEvent(data),\n        EmailVerifiedEvent: (data) =\u003e new EmailVerifiedEvent(data),\n      },\n    }),\n  ],\n  \n  providers: [\n    UserSagas,\n    ...UserQueryHandlers,\n    ...UserCommandHandlers,\n    ...UserEventHandlers,\n  ],\n})\nexport class UserModule {}\n```\n\n##### Setup async feature module\n```typescript\nimport { Module } from '@nestjs/common';\nimport { EventStoreModule } from '@juicycleff/nestjs-event-store';\nimport { EventStoreFeatureService } from './user-eventstore-feature.service';\n\n@Module({\n  imports: [\n    EventStoreModule.registerFeatureAsync({\n      useClass: EventStoreFeatureService\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n### Setup from versions below `v2.0.0`\n#### Setup root app module\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { NestjsEventStoreModule } from '@juicycleff/nestjs-event-store';\n\n@Module({\n  imports: [\n    NestjsEventStoreModule.forRoot({\n      http: {\n        port: parseInt(process.env.ES_HTTP_PORT, 10),\n        protocol: process.env.ES_HTTP_PROTOCOL,\n      },\n      tcp: {\n        credentials: {\n          password: process.env.ES_TCP_PASSWORD,\n          username: process.env.ES_TCP_USERNAME,\n        },\n        hostname: process.env.ES_TCP_HOSTNAME,\n        port: parseInt(process.env.ES_TCP_PORT, 10),\n        protocol: process.env.ES_TCP_PROTOCOL,\n      },\n    }),\n  ]\n})\nexport class AppModule {}\n```\n\n#### Setup module\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { CommandBus, CqrsModule, EventBus } from '@nestjs/cqrs';\nimport { NestjsEventStoreModule, EventStore } from '@juicycleff/nestjs-event-store';\n\nimport {\n  UserCommandHandlers,\n  UserCreatedEvent,\n  UserEventHandlers,\n  UserQueryHandlers,\n} from '../cqrs';\nimport { UserSagas } from './sagas';\n\n@Module({\n  imports: [\n    CqrsModule,\n    NestjsEventStoreModule.forFeature({\n      name: 'user',\n      resolveLinkTos: false,\n    }),\n  ],\n  \n  providers: [\n    UserSagas,\n    ...UserQueryHandlers,\n    ...UserCommandHandlers,\n    ...UserEventHandlers,\n  ],\n})\nexport class UserModule {\n  constructor(\n    private readonly command$: CommandBus,\n    private readonly event$: EventBus,\n    private readonly eventStore: EventStore,\n  ) {}\n\n  onModuleInit(): any {\n    this.eventStore.setEventHandlers(this.eventHandlers);\n    this.eventStore.bridgeEventsTo((this.event$ as any).subject$);\n    this.event$.publisher = this.eventStore;\n  }\n\n  eventHandlers = {\n    UserCreatedEvent: (data) =\u003e new UserCreatedEvent(data),\n  };\n}\n```\n\n\n## Notice\n `2.0.0` release inspired by [nestjs-eventstore](https://github.com/daypaio/nestjs-eventstore)\n\n## License\n\n  This project is [MIT licensed](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuicycleff%2Fnestjs-event-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuicycleff%2Fnestjs-event-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuicycleff%2Fnestjs-event-store/lists"}