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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verkut/common

v1.0.0

Published

Shared building blocks for NestJS applications: request-scoped context, domain primitives, error handling, API errors, HTTP filters, and utility functions.

Readme

@verkut/common

Shared building blocks for NestJS applications: request-scoped context, domain primitives, error handling, API errors, HTTP filters, and utility functions.

Installation

npm install @verkut/common
# or
pnpm add @verkut/common

Peer dependencies

npm install @nestjs/common rxjs

Context

AsyncLocalStorage-based context propagation with HTTP middleware, gRPC interceptor, and a decorator for background jobs.

Quick start

import { Module } from '@nestjs/common';
import { ContextModule } from '@verkut/common';

@Module({
  imports: [
    ContextModule.forRoot(),
  ],
})
export class AppModule {}

Module configuration

ContextModule.forRoot({
  // HTTP header to read correlation ID from (default: 'x-correlation-id')
  headerName: 'x-trace-id',

  // Custom ID generator (default: crypto.randomUUID())
  idGenerator: () => nanoid(),

  // Key name in context store (default: 'correlationId')
  contextKey: 'traceId',
});

Async configuration is also supported via forRootAsync. The module is global — import once in the root module.

HTTP context

HttpContextMiddleware is applied automatically to all routes by ContextModule. On each request it reads correlation ID from the configured header (or generates one), then wraps the request in AsyncLocalStorage.run().

gRPC context

GrpcContextInterceptor extracts correlation ID from gRPC metadata. Register it manually:

import { APP_INTERCEPTOR } from '@nestjs/core';
import { GrpcContextInterceptor } from '@verkut/common';

@Module({
  providers: [{ provide: APP_INTERCEPTOR, useClass: GrpcContextInterceptor }],
})
export class AppModule {}

@WithContext decorator

For background jobs, cron tasks, and event handlers that run outside HTTP/gRPC scope. Automatically adds className and methodName to the context:

import { WithContext } from '@verkut/common';

@Injectable()
export class PaymentProcessor {
  @WithContext()
  async processQueue() {
    // context: { className: 'PaymentProcessor', methodName: 'processQueue' }
  }

  @WithContext({ source: 'cron' })
  async runDailyReport() {
    // context: { className: 'PaymentProcessor', methodName: 'runDailyReport', source: 'cron' }
  }

  // Dynamic context — receives method arguments
  @WithContext((data) => ({ messageId: data.id }))
  async handleMessage(data: { id: string; payload: unknown }) {
    // context: { className: 'PaymentProcessor', methodName: 'handleMessage', messageId: data.id }
  }
}

If called within an existing context, parent context fields are merged.

ContextStore API

import { ContextStore } from '@verkut/common';

ContextStore.getContext();                          // shallow copy of current context
ContextStore.updateContext({ userId: '123' });      // add fields to current context
ContextStore.runWithCtx(() => { /* ... */ }, ctx);  // run in a new scope (merges with parent)
ContextStore.getRawStore();                         // raw store or undefined
ContextStore.generateId();                          // crypto.randomUUID()

Domain

Infrastructure-independent primitives for DDD-style architecture are available from a dedicated entry point:

import {
  AggregateRoot,
  ChangeTracker,
  DataTransfer,
  Entity,
  Event,
  TrackableEntity,
  UuidId,
} from '@verkut/common/domain';

| Export | Description | |--------|------------| | DataTransfer | Shallow-immutable command/query/result base with protected semantic readers | | Event | Separate shallow-immutable domain-event data base | | Entity | Minimal entity with required immutable ID and identity equality | | AggregateRoot | Entity that owns pending domain events | | ChangeTracker | Standalone shallow state tracker with from/to changes | | TrackableEntity | Optional Entity wrapper around ChangeTracker | | Identifier | Identity value-object contract and built-in ID implementations | | IUseCase | Use-case interface |

Commands, results, and events do not expose generic set() or fill() methods. Concrete types provide semantic getters and factories:

type CreateUserProps = {
  email: string;
};

class CreateUserCommand extends DataTransfer<CreateUserProps> {
  public constructor(props: CreateUserProps) {
    super(props);
  }

  public email(): string {
    return this.getOrThrow('email');
  }
}

The bases take a top-level snapshot; they deliberately do not deep-clone or deep-freeze arbitrary values. Nested state must therefore consist of immutable value objects and readonly collections. A concrete factory must copy (and may freeze) input arrays and metadata before passing them to super.

Mutable Date instances are not safe event values. Prefer an immutable time value object, an ISO-8601 string, or an epoch number. Contract tests should verify that mutating source arrays or metadata cannot change the created command or event.

There is no generic toObject() method. A concrete type may expose a deliberate toPrimitives() containing only its public contract, while persistence and outbox representations belong in explicit mappers.

Entity contains only identity. Concrete entities own their state and expose named domain behavior. Tracking is opt-in and persistence mapping belongs to infrastructure adapters.

acceptChanges() and acceptEvents() must be called only after a successful transaction commit.

ChangeTracker accepts only a plain object (including an object with a null prototype) as state. Arrays, Date, and class instances are rejected. Tracking is shallow: nested values and the from/to values in a change set must obey the same immutability contract.

Error — Domain exceptions

CoreException is the base for all domain errors. Each concrete exception has an errorCode string constant.

| Exception | Error code | Purpose | |-----------|-----------|---------| | CommonException | (abstract) | Base for project-specific exceptions | | InternalException | INTERNAL_ERROR | Unexpected internal errors | | ValidationException | VALIDATION_ERROR | Validation failures (carries validationErrors map) | | NotFoundException | NOT_FOUND | Entity not found | | ExternalApiException | EXTERNAL_API_ERROR | External API failures | | UnauthorizedException | UNAUTHORIZED | Authentication failures |

BASE_ERROR_CODES — constant with all base error code strings.

Prisma error handling

import { handlePrismaError, PRISMA_ERROR_CODES } from '@verkut/common';

handlePrismaError(error, {
  [PRISMA_ERROR_CODES.UNIQUE_CONSTRAINT]: () => new MyConflictException(),
  [PRISMA_ERROR_CODES.NOT_FOUND]: () => new MyNotFoundException(),
});

Error — API errors

API-layer error classes tied to HTTP statuses. Orthogonal to domain CoreException — these are for shaping client responses.

| Class | Status | Default message | |-------|--------|----------------| | BadRequestApiError | 400 | bad request | | UnauthorizedApiError | 401 | unauthorized | | ForbiddenApiError | 403 | forbidden | | NotFoundApiError | 404 | not found | | ConflictApiError | 409 | conflict | | InternalServerErrorApiError | 500 | internal server error | | ServiceUnavailableApiError | 503 | service unavailable |

All extend BaseApiError and implement toResponse() returning { code, message }.

Filters

HttpCoreExceptionFilter

Catches CoreException, maps errorCode → HTTP status → BaseApiError response. The errorCode → status mapping is injected via DI, so each project provides its own:

import {
  CORE_EXCEPTION_STATUS_MAPPER,
  CoreExceptionStatusMapper,
  HttpCoreExceptionFilter,
} from '@verkut/common';

@Injectable()
class MyStatusMapper implements CoreExceptionStatusMapper {
  mapToStatus(errorCode: string): number {
    if (errorCode === 'UNAUTHORIZED') return 401;
    if (errorCode.endsWith('_NOT_FOUND')) return 404;
    if (errorCode.endsWith('_ALREADY_EXISTS')) return 409;
    if (errorCode === 'VALIDATION_ERROR') return 400;
    return 500;
  }
}

@Module({
  providers: [
    { provide: CORE_EXCEPTION_STATUS_MAPPER, useClass: MyStatusMapper },
    { provide: APP_FILTER, useClass: HttpCoreExceptionFilter },
  ],
})
export class AppModule {}

HttpExceptionFilter

Catch-all fallback filter. Wraps any unhandled exception as InternalServerErrorApiError (500) and logs it as InternalException.

import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from '@verkut/common';

@Module({
  providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }],
})
export class AppModule {}

Interceptors

HttpLogRequestInterceptor

Logs incoming HTTP requests and responses (including duration and error details).

Middlewares

HttpLogRequestMiddleware

Logs HTTP request/response with method, URL, query, params, body, status code, and duration.

Utils

| Export | Description | |--------|------------| | AssertUtils | Runtime assertions (assertDefined, etc.) | | CoreAssert | Domain-level assertions that throw CoreException | | PaginationUtils | Pagination helpers (clampLimit, etc.) | | PerformanceTimer | performance.now()-based timer | | capitalize(str) | Capitalize first letter | | chunkArray(arr, size) | Split array into chunks | | flattenErrors(errors) | Flatten nested validation errors | | inspectFlat(obj) | Flat util.inspect for logging | | isNot, isEmpty, isNotEmpty, isFalsy | Predicate helpers | | isValidSortOrder, validateSortOrder | Sort order validation |

Types

| Export | Description | |--------|------------| | Nullable<T> | T \| null | | Optional<T> | T \| undefined |

Package quality checks

Run the public-package checks through Nx:

pnpm nx run @verkut/common:lint
pnpm nx run @verkut/common:typecheck
pnpm nx run @verkut/common:test-coverage
pnpm nx run @verkut/common:build

test-coverage is the required coverage-gated target. The ordinary test target remains the fast local test run. Packaging invokes a clean TypeScript build automatically, so deleted source files cannot survive in dist through incremental compilation.

License

MIT