Changelog
Every package is released at 0.4.2. The API and the requirements are those of 0.4.1.
Changed
Section titled “Changed”- Each package’s
homepage, the Homepage link on npm, is its guide on the documentation site, https://aristoteliss.github.io/nestjs-pipeline/.
Every package is released at 0.4.1. The API and the requirements are those of 0.4.0, with five more exported types.
@cqrs-ddd/mikro-orm: exportsVersionedAggregate, the entity typeoptimisticDeleteaccepts, andTimestamp, the valuesUnixTimestampTypereads.@nestjs-pipeline/core: exportsErrorClass, the key type ofLoggingBehaviorOptions.mapLogLevel.@nestjs-pipeline/resilience: exportsAnyPolicy, the policybuildResiliencePolicyreturns.@nestjs-pipeline/zod: exportsAbstractConstructor, theBaseclass type ofcreateCommand,createQueryandcreateZodRequest.
Changed
Section titled “Changed”- The manual is the documentation site, https://aristoteliss.github.io/nestjs-pipeline/; each package’s README is short and links to its guide and API reference.
- The packages’ JSDoc links resolve: the API reference of the documentation site, and the
declarations every package ships, no longer show an unresolved
import('…')link.
Every package is released at 0.4.0, as an ES module. The API and the requirements are those of 0.3.0.
Breaking
Section titled “Breaking”- Every package is published as an ES module (
"type": "module") with anexportsmap. An ES module application imports it; a CommonJS application loads it withrequire()(Node.js 22.12 or later, the packages’ minimum). A CommonJS application that compiles with TypeScriptmodule: node16moves tonodenext,node20orbundler. - Only the entry points in
exportsresolve: each package’s root,@cqrs-ddd/core’s/domain,/application,/persistenceand/http, and/package.json. Paths insidedistno longer resolve. - Packages that peer on
@nestjs-pipeline/corerequire^0.4.0of it, and@cqrs-ddd/mikro-ormrequires@cqrs-ddd/core^0.4.0.
Changed
Section titled “Changed”@nestjs-pipeline/cacheloads its optional@keyv/*store adapter throughcreateRequire, synchronously, as before.- The release check loads every package from a CommonJS and an ES module consumer,
type-checks them with TypeScript
Bundlerresolution, and loads every entry point in Bun throughimportandrequire().
Every package is released at 0.3.0, for NestJS 12.
Requirements for every package
Section titled “Requirements for every package”- Node.js 22.12 or later (
engines): a CommonJS application loads the ES modules NestJS 12 publishes through Node’srequire()of ES modules.@cqrs-ddd/mikro-ormrequires 22.17, as its@mikro-orm/core7 peer does. - NestJS
^12.1.0(@nestjs/common,@nestjs/core,@nestjs/cqrs) for every@nestjs-pipeline/*package that peers on NestJS. NestJS 11 and 12.0.x are not supported: 12.0.x drops the@Optional()markers of a base class in a subclass that declares no constructor of its own. - Packages that peer on
@nestjs-pipeline/corerequire^0.3.0of it. - A CommonJS application that compiles with TypeScript
module: node16moves tonodenext,node20orbundler;node16refuses imports of ES modules (TS1479).
Breaking
Section titled “Breaking”@nestjs-pipeline/zod:ZodPipeis removed. Declare the schema with{ schema }on@Body,@Paramor@Query, and register Nest’sStandardSchemaValidationPipeonce withexceptionFactory: zodBadRequest.@nestjs-pipeline/resilience: requirescockatiel^4.0.0, whose policies report errors asunknown.
@nestjs-pipeline/zod:zodBadRequest, the exception factory for Nest’sStandardSchemaValidationPipe; it answers 400 with the bodyZodValidationFiltergives.@cqrs-ddd/core:IAggregateRoot, the event-buffering contract, matching NestJS 12’s, which this package’s and NestJS’s aggregates both satisfy.AggregateRoot’spublish,publishAllandcommittake an optional dispatcher context and return the publisher’s result;commithands over a copy of the events and clears the buffer oncepublishAllreturns.IDomainEventPublisher.publishAll(events, dispatcherContext?).@nestjs-pipeline/job-context:JobContextModule.forRoot’stenantsalso takes a function, called once when the application builds its providers, so the list can come from configuration read at startup.
Changed
Section titled “Changed”@cqrs-ddd/core:CommandBaseHandleraccepts anyIAggregateRootresult and passes the aggregate topublishAllas the dispatcher context, as NestJS’sEventPublisherdoes.@nestjs-pipeline/core: handlers are discovered through Nest’sDiscoveryServiceand the metadata key each public@nestjs/cqrshandler decorator records, because@nestjs/cqrs12 exports only its package root. The bootstrap fails when a decorator records other than one key.@nestjs-pipeline/idempotency: NestJS 12’s default exception filter answers a plainErrorthat carries astatusCodewith 500, so registerIdempotencyConflictFilterto keep the 409 and 422 answers.@nestjs-pipeline/rate-limit: tested with rate-limiter-flexible 11, which throws when a limiter is created without a finitepointsorduration.
@cqrs-ddd/core:CommandBaseHandler.execute()awaits whatpublishAll()returns, after clearing the buffer, so a publisher that rejects rejects the command. The rejection was unhandled, and Node exited the process.@nestjs-pipeline/audit,/cache,/deadletter,/feature-flags,/idempotency,/rate-limit,/resilience(behavior andResiliencePolicies) and core’sLoggingBehavior: with the default Nest logger, each record prints its context once; the class name was printed again as a record of its own. The fallbackLoggerhas no context and no timestamp delta of its own.@cqrs-ddd/mikro-orm:engines.nodeis>=22.17.0, the minimum of its@mikro-orm/core7 peer.
A release of @nestjs-pipeline/zod, /casl, /feature-flags, /idempotency and
/rate-limit; the others keep their versions. Unlike 0.2.1, it needs a code change where
the exception filters are registered.
Changed
Section titled “Changed”- The exception filters
ZodValidationFilter,UnauthorizedActionFilter,FeatureDisabledFilter,IdempotencyConflictFilterandRateLimitExceededFiltertake Nest’sHttpAdapterHostas their first constructor argument and answer throughhttpAdapter.reply(RateLimitExceededFiltersetsRetry-AfterwithhttpAdapter.setHeader). Register them as{ provide: APP_FILTER, useClass: X }, or passapp.get(HttpAdapterHost)touseGlobalFilters(new X(...)).FeatureDisabledFilter’s options are its second argument. - The five packages declare
@nestjs/core^11.0.0as a peer dependency.
- On Fastify, a package error thrown in Nest middleware reached its filter with the raw
Node response; the filter threw
TypeError: response.status is not a functionand the request got no answer. The filters now answer it.
A patch release of the ten packages below; the others stay at 0.2.0. Every change is an addition or a documentation fix: no export, signature, behavior or peer range of 0.2.0 changes, so upgrading needs no code change.
@cqrs-ddd/core:requireTenant(purpose, source?)returns the tenant for a security-sensitive operation, fromsourceor the registered resolver, and throwsMissingTenantContextErrorwhen there is none.requireTenantId(source, purpose), the same with the arguments reversed, is deprecated and calls it.@nestjs-pipeline/casl:abilityDigest(context?), the SHA-256 of the effective rules (conditions resolved against the principal), for cache-key scopes and idempotency replay scopes;requireAbilityDigest(context?), which throws the newMissingAbilityErrorinstead of returningundefined;CaslAuthorizer.dependsOnEntity(action, subject), whether a conditional rule decides on entity attributes. Adds@cqrs-ddd/safe-stringifyas a dependency.@nestjs-pipeline/opentelemetry:AttributesBehaviorwithAttributesBehaviorOptions(factories). It runs attribute factories once the rest of the chain has finished, successfully or not, and adds the result to the attribute bag thatTraceBehaviorandMetricsBehaviorread. A failing factory contributes nothing; the others still apply.- Attribute builders, each in
src/helpers/build-attributes.ts, returning{}when their behavior did not run and never a cache, idempotency or rate-limit key:@nestjs-pipeline/cache:buildCacheAttributes→cache.hit.@nestjs-pipeline/idempotency:buildIdempotencyAttributes→idempotency.replayed,idempotency.ownership_lost.@nestjs-pipeline/rate-limit:buildRateLimitAttributes→rate_limit.remaining_points.@nestjs-pipeline/feature-flags:buildFeatureFlagAttributes→feature_flag.key,feature_flag.enabled,feature_flag.variant,feature_flag.reason,feature_flag.error_code.@nestjs-pipeline/deadletter:buildDeadLetterAttributes→dead_letter.captured.
Documentation
Section titled “Documentation”@nestjs-pipeline/zod: examples usez.email()andz.uuid()instead of the deprecatedz.string().email()andz.string().uuid().@nestjs-pipeline/audit: the actor example readsgetSessionPrincipal().@nestjs-pipeline/feature-flags: the module example usesTypedInMemoryProvider(@openfeature/server-sdk1.23+;InMemoryProviderbefore it).
Every package is released at 0.2.0. Five were on npm before; thirteen are released for the first time.
Requirements for every package
Section titled “Requirements for every package”- Node.js 22 or later (
engines). - NestJS 11 for every
@nestjs-pipeline/*package that peers on NestJS (@nestjs-pipeline/tenanthas no peers). NestJS 10 is no longer supported. - Packages that peer on
@nestjs-pipeline/corerequire^0.2.0of it instead of any version.
Upgrading from 0.1.x
Section titled “Upgrading from 0.1.x”The README’s Upgrading from 0.1.x shows the common changes with before-and-after code.
@nestjs-pipeline/core (from 0.1.18)
Section titled “@nestjs-pipeline/core (from 0.1.18)”Breaking:
- Peers are
@nestjs/common,@nestjs/coreand@nestjs/cqrs^11.0.0. - Removed from the public API:
PipelineBootstrapService,PIPELINE_MODULE_OPTIONS,PIPELINE_OPTIONS_REGISTRY,clearPipelineOptionsRegistry,SET_RESPONSEandSET_ORIGINAL_CORRELATION_ID. Configure the pipeline throughPipelineModule.forRootorforRootAsync; a behavior can no longer set a context’s response or original correlation ID. context.correlationIdis read-only, andoriginalCorrelationIdis removed: a behavior can no longer replace the correlation ID of a running pipeline. Set it where the work enters (HttpCorrelationMiddleware,@WithCorrelation,runWithCorrelationId).- The module options
correlationIdFactoryandcorrelationIdRunnerare removed. A pipeline takes its tenant and correlation id from thesourcesmodule option when it starts (tenantSourceof@nestjs-pipeline/tenant,correlationSourceof@nestjs-pipeline/correlation), or from the pipeline it is nested in, generates auuidv7()correlation id when there is none, and runs its behaviors inside both values. Set them where work enters the application:runWithTenantof@nestjs-pipeline/tenant,HttpCorrelationMiddlewareorrunWithCorrelationIdof@nestjs-pipeline/correlation. - The new
diagnosticsoption defaults to'strict': a handler whose pipeline does not meet a behavior’sPIPELINE_BEHAVIOR_CONTRACTmakes bootstrap throw aPipelineConfigurationError. Pass'warn'or'off'to relax it. loggerProvideris typedPipelineLoggerProviderinstead of anyProvider: itsprovidemust beLOGGING_BEHAVIOR_LOGGER.@cqrs-ddd/uuidv7,@cqrs-ddd/untypedand@cqrs-ddd/safe-stringifyare new runtime dependencies.getBehaviorId(cls)returns the class itself when noPIPELINE_BEHAVIOR_IDis set, instead ofcls.name, so two behaviors with the same class name no longer collide.PIPELINE_BEHAVIOR_IDisSymbol.for('@nestjs-pipeline/core:PIPELINE_BEHAVIOR_ID')instead of a localSymbol, so it matches across duplicate copies of core.- When several NestJS applications in one process wrap the same handler class, calling it on an instance that none of them created throws, instead of running without any pipeline.
LoggingBehaviormasks sensitive fields in logged payloads by default (redactSensitiveKeys: true, usingDEFAULT_REDACT_KEYS). Key matching ignores case,_and-, sorefreshTokenalso masksrefresh_token.excludeKeysalso applies to the properties of clonedErrorobjects and toMapkeys.
Added:
PipelineModule.forRootAsync(PipelineModuleAsyncOptions,PipelineOptionsFactory,PipelineLoggerProvider),PipelineModuleFeatureOptions, thediagnosticsoption, thelogging()intent helper and@SkipPipeline.- Pipeline items:
createPipelineItem,getPipelineItem,setPipelineItem,hasPipelineItem,requirePipelineItem,MissingPipelineItemError. - Behavior contracts and bootstrap diagnostics:
PIPELINE_BEHAVIOR_CONTRACT,PipelineConfigurationErrorand their types. context.tenantId, the tenant of the execution; it is write-once, so assigning a different tenant throws.SET_TENANT_IDsets it in a custom runner.LoggingBehavioroptionsredactKeysandredactSensitiveKeys.toPostgresJson.tenantSegmentsandTenantPartitionOptions, the tenant part of the cache, idempotency and rate-limit key factories, andMissingPartitionError, the base of their partition errors. The cache key factory now also takesincludeTenant.- The
sourcesmodule option withContextSourceandContextSources: where pipelines take their tenant and correlation id from. Bootstrap warns when it is omitted; passsources: {}to run without sources on purpose.
Removed from the public API: uuidv7, isUuidV7 and untyped. Import them from
@cqrs-ddd/uuidv7 and @cqrs-ddd/untyped. The serializers, which 0.1.18 did not export,
are public in @cqrs-ddd/safe-stringify.
@nestjs-pipeline/correlation (from 0.1.8)
Section titled “@nestjs-pipeline/correlation (from 0.1.8)”Breaking:
- Peer:
@nestjs/common^11.0.0(was^10.0.0 || ^11.0.0). It still depends on no pipeline package;@cqrs-ddd/uuidv7and@cqrs-ddd/untypedare new dependencies. setCorrelationFallbackanduuidv7are no longer exported. Importuuidv7from@cqrs-ddd/uuidv7, which has the same API and output.correlationStoreis replaced bycorrelationSource. Pass it toPipelineModule.forRoot({ sources: { correlationId: correlationSource } })so a pipeline takes the id andgetCorrelationId()in a handler returns the pipeline’s id.runWithCorrelationId,getCorrelationId,correlationHeadersand@WithCorrelationkeep their API.HttpCorrelationMiddlewaresets the correlation header on the response, lowercases the configured header name and throws at construction on an invalid one. New options:acceptIncoming,trimIncoming,maxLengthandvalidateIncoming.addCorrelationIdthrows aTypeErrorfor any value that is not a plain object (class instances included), not only for arrays.- An incoming correlation ID longer than 128 characters, or not matching
DEFAULT_CORRELATION_ID_PATTERN, is discarded and replaced by a locally generated ID.
Added: correlationSource, DEFAULT_CORRELATION_HEADER,
DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN.
@nestjs-pipeline/opentelemetry (from 0.1.8)
Section titled “@nestjs-pipeline/opentelemetry (from 0.1.8)”Breaking: @nestjs/common ^11.0.0; @nestjs-pipeline/core ^0.2.0.
TraceBehaviorno longer implementsonModuleInit, no longer injects a logger and no longer checks whether an SDK is registered.TraceBehaviorOptionsis exported as a type only.- A tracer, a meter or an enrichment callback that throws never replaces the handler’s result or error, and never runs the handler twice.
Added: spans carry pipeline.tenant_id, pipeline.outcome and error.type;
MetricsBehavior records a pipeline.handler.active counter and labels instruments with
outcome and pipeline.outcome. Also added: MetricsBehavior and metrics(), trace(), buildTraceAttributes,
buildMetricAttributes, addPipelineTelemetryAttributes,
getPipelineTelemetryAttributes, PIPELINE_OTEL_ATTRIBUTES,
PIPELINE_TELEMETRY_ATTRIBUTES and their types.
@nestjs-pipeline/zod (from 0.1.6)
Section titled “@nestjs-pipeline/zod (from 0.1.6)”Breaking:
- Peers:
zod^4.3.0(was^4.0.0),@nestjs/common^11.0.0,@nestjs-pipeline/core^0.2.0. ZOD_SCHEMA, the deprecated alias, is removed; useZOD_SCHEMA_KEY.ZodValidationBehaviorapplies the parsed output to the request instead of only validating: it parses withsafeParseAsync, deletes keys the schema strips and assigns coerced and defaulted values before the handler runs. A top-level output that is not a plain object is rejected with aTypeError, as is a non-object request with a schema.ZodPipe.transform()returns aPromiseand parses asynchronously.
Added:
createCommand,createQueryandcreateZodRequest, withInferInput,InferOutputand the class types.updatableandupdatableFieldsOf: mark a command field in its schema, andcreateCommand()lists the marked fields asupdatableFields.createZodMapper;getRawInput,getValidatedData,ZOD_RAW_INPUT_KEY,ZOD_VALIDATED_DATA_KEY.
@nestjs-pipeline/casl (from 0.1.1)
Section titled “@nestjs-pipeline/casl (from 0.1.1)”Breaking:
- Peers:
@casl/ability^7.0.0(was^6.0.0),@nestjs/common^11.0.0,@nestjs-pipeline/core^0.2.0. - The provider API is replaced by one permission source. Removed:
CASL_ROLE_PROVIDER,CASL_USER_CAPABILITY_PROVIDER,CASL_USER_CONTEXT_RESOLVER,CASL_USER_CONTEXT_KEY,CASL_SUBJECT_CONTEXT_PATHS,CASL_FIELDS_FROM_REQUEST,CASL_BEHAVIOR_LOGGER,IRoleProvider,IUserCapabilityProvider,IUserContextResolver,StaticRoleProvider,CaslUserContext,RoleDefinition,UserCapabilities,buildAbilityFromRules,capabilityToRawRuleandcapabilitiesToRawRules. ImplementICaslPermissionSource, whoseload()returns{ principal, rules }ornull, and register it withCaslModule.forRoot({ permissionSource }). buildAbility(roles, user, additional, denied)becomesbuildAbility(rules, principal).CaslBehaviorOptionslosessubjectFromRequest,subjectContextPaths,fieldsFromRequest,skipCheckandprebuiltAbility;rulesis required and non-empty (requires()builds it).CaslBehaviorno longer takes a logger.- A denial throws
UnauthorizedActionException(it extendsError) instead of NestJS’sForbiddenException; registerUnauthorizedActionFilterto answer HTTP 403. - A rule condition whose placeholder resolves to an object throws, because CASL would read the object as query operators.
Added: requires(), CaslAuthorizer (can, authorize, project),
UnauthorizedActionException and UnauthorizedActionFilter (HTTP 403),
getCaslAbility, getCaslPrincipal, hasEntityConditions, CASL_PERMISSION_SOURCE,
CASL_PRINCIPAL_KEY, CASL_BEHAVIOR_ID, CASL_ACTIONS, CASL_SUBJECTS and their types.
First releases
Section titled “First releases”@nestjs-pipeline/audit: records every audited request, on success and failure, to anAuditSink(console by default, Postgres bundled), with payload redaction. Only commands are audited unlesscaptureKindslists queries or events. A sink that implementsbegin(the Postgres sink does) receives a pending record before the handler runs, then the final record under the same id, so a process stop leaves apendingrow instead of none. With the defaultfailOpen: true, a sink failure is logged and the request’s outcome is kept. WithfailOpen: false, a sink failure fails a successful request; if the handler had already failed, its own error is rethrown unchanged and the sink failure is logged.@nestjs-pipeline/cache: read-through caching for queries on cache-manager 7 and Keyv.keyis required: there is no default key.@nestjs-pipeline/deadletter: captures failed requests through aDeadLetterTransport(BullMQ, RabbitMQ and Postgres bundled); events by default, commands and queries when listed incaptureKinds. The Postgres transport is aDeadLetterStore, andDeadLetterRedriverreplays a stored record, counts failed attempts and resolves it; a redacted payload is not replayed without arebuild.rethrow: falseswallows an event handler’s error only, and is a bootstrap error on a command or query handler. The RabbitMQ transport is tested with a mocked channel only.@nestjs-pipeline/feature-flags: gates handlers through OpenFeature.FeatureDisabledFilteranswers HTTP 403, or 404 with{ status: 404 }to hide the feature.allowedVariantsgates on a variant, anderrorPolicy('use-default'or'throw') decides what a provider error does.@nestjs-pipeline/idempotency: concurrent-duplicate exclusion and replay of successful responses, with memory (default), Redis and Postgres stores. A failed execution releases its key by default. The Postgres store keeps responses as JSON text, so every response, including one with NUL characters or unpaired surrogates, replays exactly. Keys take core’sincludeTenantandrequireTenant, andMissingIdempotencyPartitionErrorextends core’sMissingPartitionError.@nestjs-pipeline/rate-limit: per-command rate limiting on rate-limiter-flexible, applied wherever the command is dispatched from, with an HTTP 429 filter.keyFactoryis required: there is no default bucket.pointsis a fixed or computed cost per command;0charges nothing.@nestjs-pipeline/resilience: resilience on cockatiel, in two parts. Named policies for outbound dependencies (retry, circuit breaker, timeout, bulkhead, fallback), declared inResilienceModule.forRoot({ policies })orforRootAsync, built and validated at startup, and shared throughResiliencePoliciesor@InjectResiliencePolicy(name).ResilienceBehaviorapplies retry, timeout and bulkhead around a whole handler; a circuit breaker or fallback there is a bootstrap error. Retry, circuit breaker and fallback require ahandlepredicate orhandleAllErrors: true.@nestjs-pipeline/tenant:runWithTenant()sets the current tenant andcurrentTenantId()reads it;tenantSourcehands the same store toPipelineModule.forRoot({ sources })andJobContextModule.forRoot. It has no dependencies.@nestjs-pipeline/job-context: carries a request’s tenant, correlation id and principal identity into the queue jobs it enqueues (withJobContext,@InJobContext), re-checked through an applicationIJobPrincipalwhen the job runs, and gives system work an explicit principal and grants per tenant (@AsSystem). It reads and restores the tenant and correlation id through thesourcesofJobContextModule.forRootand depends on no other pipeline package.@cqrs-ddd/core: framework-neutral DDD building blocks (aggregates, domain events,CommandBaseHandler, repository contracts, persistence lifecycle decorators that map unique violations by entity property through a pluggable persistence dialect (IPersistenceDialect,setPersistenceDialect), a revision-fenced repository cache (CACHE_TOKEN, withMemoryCachefor one process), tenant-scoped cache keys, HTTP status mapping, and the value rulestextRuleandnumberRule, which throw anInvalidValueExceptionor an application’s own subclass). It depends on no framework:CommandBaseHandlertakes anyIDomainEventPublisher, the cache decorators take alogger, and cache keys take their tenant from a resolver the application registers withsetTenantResolver. It has no peers and no ORM dependency.@cqrs-ddd/mikro-orm: the MikroORM 7 adapters of@cqrs-ddd/core—AggregateRepository,optimisticUpdate,optimisticDelete,assertAutocommit,MikroOrmDialect(reads the violated unique constraint from the ORM metadata, for PostgreSQL and SQLite),mapPersistenceError/isTransientPersistenceError,MikroOrmCache(withCacheEntrySchemaandcreateCacheTableSqlfor its table),TenantStore(the active tenant’sEntityManager, with a database or a schema per tenant) andUnixTimestampType, which throws aTypeErrorfor a value with no valid time.@cqrs-ddd/coreand@mikro-orm/coreare required peers.@cqrs-ddd/uuidv7: RFC 9562 UUIDv7 generation and validation, with no dependencies.@cqrs-ddd/untyped:untyped(value), a typed replacement foras anythat reads undeclared properties asunknown; no dependencies.@cqrs-ddd/safe-stringify: a strict, key-sorted serializer for identities and a safe, redacting serializer for logs, with the key-segment helpers; no dependencies. Its output is frozen, so stored cache keys stay valid.