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

@syncafricabs/kernspark-nestjs

v1.0.0

Published

NestJS adapter for @syncafricabs/kernspark-core.

Readme

@syncafricabs/kernspark-nestjs

License: Apache-2.0 TypeScript NestJS

NestJS adapter for the SyncAfrica KernSpark ecosystem. This package provides production-ready NestJS exception filters, interceptors, and module definitions that integrate the framework-independent @syncafricabs/kernspark-core with NestJS.

Table of Contents

What is it?

@syncafricabs/kernspark-nestjs is a framework adapter that bridges the SyncAfrica KernSpark Core with NestJS. It provides:

  • NestJsExceptionFilter - Global exception filter that catches ApplicationError instances and converts them to standardized JSON API responses
  • NestJsResponseInterceptor - Response interceptor that wraps successful responses in the ApiSuccess envelope
  • NestJsModule - NestJS module definition that registers filters and interceptors globally

The adapter follows the adapter pattern: it depends on @syncafricabs/kernspark-core (the framework-independent layer) and NestJS only in the adapter layer. The core has zero framework dependencies.

Why it exists

Modern microservice architectures benefit from a KernSpark - a common set of contracts, types, and behaviors shared across bounded contexts. The SyncAfrica KernSpark provides:

  1. Standardized API envelopes (ApiSuccess / ApiError)
  2. Rich domain error hierarchy (ValidationError, BusinessError, AuthenticationError, etc.)
  3. Result types (Result, Ok, Err)
  4. Domain primitives (UUID, Money, Entity, ValueObject)

Without adapters, each team would reimplement framework-specific plumbing to use these core types. This adapter eliminates that duplication by providing NestJS-specific bindings.

Features

  • Global exception filtering - Catch all ApplicationError instances with a single filter
  • Standardized JSON responses - All responses follow the ApiSuccess / ApiError envelope
  • Response interception - Automatically wrap successful responses
  • Module-based registration - Clean NestJS module integration
  • Full TypeScript support - Comprehensive type definitions with decorators
  • Production-ready - Proper error handling, stack traces, cause chains
  • Framework isolation - Core has no NestJS dependencies

Installation

npm install @syncafricabs/kernspark-nestjs @syncafricabs/kernspark-core @nestjs/common @nestjs/core @nestjs/platform-express reflect-metadata rxjs

Or with your preferred package manager:

yarn add @syncafricabs/kernspark-nestjs @syncafricabs/kernspark-core @nestjs/common @nestjs/core @nestjs/platform-express reflect-metadata rxjs

Quick Start

import { Module } from '@nestjs/common';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { NestJsExceptionFilter, NestJsResponseInterceptor, KernsparkNestJsModule } from '@syncafricabs/kernspark-nestjs';
import { ValidationError, NotFoundError } from '@syncafricabs/kernspark-core';

@Module({
  imports: [
    KernsparkNestJsModule.forRoot({
      filterOptions: { logErrors: true, includeStack: false },
      interceptorOptions: { defaultMessage: 'OK', envelopeSuccess: true },
      global: true,
    }),
  ],
  providers: [
    {
      provide: APP_FILTER,
      useClass: NestJsExceptionFilter,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: NestJsResponseInterceptor,
    },
  ],
})
export class AppModule {}
import { Controller, Get, Param, Post, Body, Inject } from '@nestjs/common';
import { NotFoundError, ConflictError } from '@syncafricabs/kernspark-core';

@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    const user = findUser(id);
    if (!user) {
      throw new NotFoundError('User not found');
    }
    return user;
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    if (userExists(createUserDto.email)) {
      throw new ConflictError('User already exists');
    }
    return createUser(createUserDto);
  }
}

Comprehensive Usage

Exception Filter

The NestJsExceptionFilter is a global exception filter that catches all unhandled exceptions and maps them to standardized JSON responses following the ApiError envelope.

Basic Setup

import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { NestJsExceptionFilter } from '@syncafricabs/kernspark-nestjs';

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

Custom Exception Filter Configuration

import { NestJsExceptionFilter, NestJsExceptionFilterOptions } from '@syncafricabs/kernspark-nestjs';

const options: NestJsExceptionFilterOptions = {
  logErrors: true,
  logStack: true,
  includeCause: true,
  includeStack: process.env.NODE_ENV === 'development',
  defaultStatusCode: 500,
  logger: (error, req) => {
    console.error({
      correlationId: req.headers['x-correlation-id'],
      method: req.method,
      url: req.originalUrl,
      error: error.message,
      stack: error.stack,
    });
  },
};

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useValue: new NestJsExceptionFilter(options),
    },
  ],
})
export class AppModule {}

Throwing Errors from Controllers

import {
  Controller,
  Get,
  Post,
  Body,
  Param,
  UseGuards,
  Request,
} from '@nestjs/common';
import {
  ValidationError,
  MissingFieldsError,
  BusinessError,
  ConflictError,
  NotFoundError,
  AuthenticationError,
  InvalidTokenError,
  AuthorizationError,
  PermissionDeniedError,
  InfrastructureError,
  ExternalServiceError,
} from '@syncafricabs/kernspark-core';

@Controller('users')
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    const { name, email } = createUserDto;

    if (!name || !email) {
      throw new MissingFieldsError('Name and email are required');
    }

    if (!isValidEmail(email)) {
      throw new ValidationError('INVALID_EMAIL', 'Email format is invalid');
    }

    if (userExists(email)) {
      throw new ConflictError('User with this email already exists');
    }

    const user = createUser(createUserDto);
    return user;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    const user = findUser(id);
    if (!user) {
      throw new NotFoundError('User not found');
    }
    return user;
  }

  @Delete(':id')
  @UseGuards(JwtAuthGuard)
  remove(@Request() req, @Param('id') id: string) {
    if (!req.user) {
      throw new InvalidTokenError('Invalid or expired token');
    }

    if (!req.user.canDeleteUsers) {
      throw new PermissionDeniedError('You do not have permission to delete users');
    }

    deleteUser(id);
    return { message: 'User deleted successfully' };
  }

  @Get('external/:id')
  async fetchExternal(@Param('id') id: string) {
    try {
      const data = await externalService.fetch(id);
      return data;
    } catch (error) {
      throw new ExternalServiceError('Failed to fetch data from external service');
    }
  }
}

Response Interceptor

The NestJsResponseInterceptor wraps all successful responses in the ApiSuccess envelope, ensuring consistent response formatting across your API.

Basic Setup

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { NestJsResponseInterceptor } from '@syncafricabs/kernspark-nestjs';

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

Custom Interceptor Configuration

import { NestJsResponseInterceptor, NestJsResponseInterceptorOptions } from '@syncafricabs/kernspark-nestjs';

const options: NestJsResponseInterceptorOptions = {
  defaultMessage: 'Success',
  envelopeSuccess: true,
};

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useValue: new NestJsResponseInterceptor(options),
    },
  ],
})
export class AppModule {}

Bypassing the Interceptor

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { NestJsResponseInterceptor } from '@syncafricabs/kernspark-nestjs';
import { FileInterceptor } from '@nestjs/platform-express';

@Controller('files')
export class FilesController {
  @Get('raw')
  @UseInterceptors(FileInterceptor('file'))
  getRawFile() {
    return rawBuffer;
  }

  @Get('stream')
  @UseInterceptors(StreamInterceptor)
  getStream() {
    return stream;
  }
}

class StreamInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    return next.handle();
  }
}

Module Setup

The KernsparkNestJsModule provides a clean way to register all KernSpark functionality in your NestJS application.

Synchronous Module Registration

import { Module } from '@nestjs/common';
import { KernsparkNestJsModule } from '@syncafricabs/kernspark-nestjs';

@Module({
  imports: [
    KernsparkNestJsModule.forRoot({
      filterOptions: {
        logErrors: true,
        logStack: process.env.NODE_ENV === 'development',
        includeCause: true,
        includeStack: process.env.NODE_ENV === 'development',
      },
      interceptorOptions: {
        defaultMessage: 'OK',
        envelopeSuccess: true,
      },
      global: true,
    }),
  ],
})
export class AppModule {}

Asynchronous Module Registration

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { KernsparkNestJsModule } from '@syncafricabs/kernspark-nestjs';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    KernsparkNestJsModule.forRootAsync({
      useFactory: (configService: ConfigService) => ({
        filterOptions: {
          logErrors: configService.get('NODE_ENV') === 'development',
          logStack: configService.get('NODE_ENV') === 'development',
          includeCause: true,
          includeStack: configService.get('NODE_ENV') === 'development',
          logger: (error, req) => {
            console.error({
              correlationId: req.headers['x-correlation-id'],
              method: req.method,
              url: req.originalUrl,
              error: error.message,
            });
          },
        },
        interceptorOptions: {
          defaultMessage: configService.get('API_DEFAULT_MESSAGE', 'OK'),
          envelopeSuccess: true,
        },
        global: true,
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Feature Module Integration

import { Module } from '@nestjs/common';
import { KernsparkNestJsModule } from '@syncafricabs/kernspark-nestjs';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    KernsparkNestJsModule.forRoot({
      filterOptions: { logErrors: true },
      interceptorOptions: { envelopeSuccess: true },
      global: false,
    }),
    UsersModule,
  ],
})
export class AppModule {}

API Reference

NestJsExceptionFilter

Global exception filter implementing NestJS ExceptionFilter interface.

| Option | Type | Default | Description | |--------|------|---------|-------------| | logErrors | boolean | true | Enable/disable error logging | | logStack | boolean | false | Include stack traces in logs | | includeCause | boolean | true | Include error cause in response | | includeStack | boolean | false | Include stack traces in response (dev only) | | defaultStatusCode | number | 500 | Default status for unknown errors | | logger | function | console.error | Custom logger function |

Response Format:

{
  "status": 400,
  "success": false,
  "errorCode": "MISSING_FIELDS",
  "message": "Name and email are required",
  "data": { "cause": "Validation chain failed" }
}

NestJsResponseInterceptor

NestJS interceptor that wraps successful responses in ApiSuccess envelope.

| Option | Type | Default | Description | |--------|------|---------|-------------| | defaultMessage | string | 'OK' | Default message for responses | | envelopeSuccess | boolean | true | Wrap responses in envelope |

Response Format (enabled):

{
  "status": 200,
  "success": true,
  "message": "OK",
  "data": { "id": 1, "name": "John" }
}

Response Format (disabled):

{
  "id": 1,
  "name": "John"
}

KernsparkNestJsModule

NestJS module for registering KernSpark functionality.

forRoot(options)

Synchronous module registration.

KernsparkNestJsModule.forRoot({
  filterOptions?: NestJsExceptionFilterOptions,
  interceptorOptions?: NestJsResponseInterceptorOptions,
  global?: boolean,
});

forRootAsync(options)

Asynchronous module registration with dependency injection.

KernsparkNestJsModule.forRootAsync({
  useFactory: (configService: ConfigService) => ({
    filterOptions: { /* ... */ },
    interceptorOptions: { /* ... */ },
  }),
  inject: [ConfigService],
});

Error Handling Reference

The exception filter maps the following ApplicationError subclasses:

| Error Class | HTTP Status | Error Code | |-------------|-------------|------------| | ValidationError | 400 | Custom (e.g., INVALID, MISSING_FIELDS) | | MissingFieldsError | 400 | MISSING_FIELDS | | BusinessError | Varies | Custom | | ConflictError | 409 | CONFLICT | | InsufficientFundsError | 400 | INSUFFICIENT_FUNDS | | QuotaExceededError | 429 | QUOTA_EXCEEDED | | NotFoundError | 404 | NOT_FOUND | | ExpiredError | 410 | EXPIRED | | TooManyRequestsError | 429 | TOO_MANY_REQUESTS | | PaymentRequiredError | 402 | PAYMENT_REQUIRED | | LockedError | 423 | LOCKED | | AccountSuspendedError | 403 | ACCOUNT_SUSPENDED | | FeatureNotAvailableError | 501 | FEATURE_NOT_AVAILABLE | | DataIntegrityError | 422 | DATA_INTEGRITY_ERROR | | AuthenticationError | 401 | Custom (e.g., INVALID_TOKEN) | | InvalidTokenError | 401 | INVALID_TOKEN | | TokenExpiredError | 401 | TOKEN_EXPIRED | | SessionExpiredError | 401 | SESSION_EXPIRED | | AuthorizationError | Varies | Custom | | PermissionDeniedError | 403 | PERMISSION_DENIED | | NotAllowedError | 403 | NOT_ALLOWED | | InfrastructureError | Varies | Custom | | ExternalServiceError | 502 | EXTERNAL_SERVICE_ERROR | | BadGatewayError | 502 | BAD_GATEWAY | | GatewayTimeoutError | 504 | GATEWAY_TIMEOUT | | ServiceUnavailableError | 503 | SERVICE_UNAVAILABLE | | RequestFailedError | 500 | REQUEST_FAILED | | NotImplementedError | 501 | NOT_IMPLEMENTED | | MaintenanceModeError | 503 | MAINTENANCE_MODE |

Unknown ApplicationError instances use their embedded statusCode. NestJS HttpException instances are preserved with their original status code and message.

TypeScript Support

Full TypeScript support with strict mode enabled and decorator metadata support.

import { Module } from '@nestjs/common';
import { NestJsExceptionFilter, NestJsResponseInterceptor } from '@syncafricabs/kernspark-nestjs';

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

Decorator Metadata

Ensure your tsconfig.json includes:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Compatibility Matrix

| Package Version | Node.js | NestJS | @syncafricabs/kernspark-core | |----------------|---------|--------|----------------------------------| | 1.0.0 | >=18.0.0 | ^9.0.0 || ^10.0.0 | ^1.0.0 |

| Feature | NestJS 9.x | NestJS 10.x | |---------|------------|-------------| | Exception Filter | Supported | Supported | | Response Interceptor | Supported | Supported | | Dynamic Module | Supported | Supported | | Async Configuration | Supported | Supported | | TypeScript 5.0+ | Supported | Supported |

Contributing

  1. Fork the repository
  2. Create your feature branch: git checkout -b feature/my-feature
  3. Commit your changes: git commit -am 'Add my feature'
  4. Push to the branch: git push origin feature/my-feature
  5. Submit a pull request

Development Setup

# Clone the repository
git clone https://github.com/iamprovy-dev/kernspark-js.git
cd kernspark/packages/kernspark-nestjs

# Install dependencies
npm install

# Build
npm run build

# Run lint
npm run lint

# Run tests
npm test

Code Standards

  • Use TypeScript with strict mode
  • Follow the existing code style (2-space indentation, semicolons)
  • Add JSDoc comments for public APIs
  • Ensure all tests pass before submitting PR

License

Apache-2.0

Author

Providence Chikukwa