Skip to content

NestJS

Import from @system-core/core/integrations/nestjs.

Use this integration when the host application is NestJS and you want to bridge Nest execution context to the system-core HTTP adapter model. The adapter has zero @nestjs/* imports — it relies on structural typing, so the package installs without requiring NestJS in the dependency tree.

Use Cases

  • NestJS controllers calling package-owned auth or CMS flows
  • Guards that validate Bearer tokens through the system-core auth system
  • A single SystemCoreModule.forRoot() that registers the runtime as injectable providers
  • Exception filters that map system-core errors to NestJS HTTP responses

How It Works

system-core's HttpAdapter interface uses event: unknown as the universal integration point. Every framework adapter implements this same interface against its own request/response pair.

The NestJS adapter wraps NestJS's ExecutionContext into a { request, response } object that satisfies the HttpAdapter contract. All package-owned logic functions — auth middleware, rate limiting, CMS helpers — work unchanged through it.

text
ExecutionContext → getNestjsEvent(ctx) → NestjsHttpContext → nestjsAdapter → HttpAdapter

Basic Setup

1 — Install

bash
npm install @system-core/core zod @prisma/client
npm install bcryptjs jsonwebtoken nodemailer

2 — Register the runtime module

ts
// app.module.ts
import { Module } from '@nestjs/common'
import { SystemCoreModule } from '@system-core/core/integrations/nestjs'
import { nestjsAdapter } from '@system-core/core/integrations/nestjs'
import { createSystem } from '@system-core/core'
import { prismaClient } from './prisma'

const system = await createSystem({ prisma: prismaClient })

@Module({
  imports: [
    SystemCoreModule.forRoot({
      http: nestjsAdapter,
      auth: system.auth,
      cms:  system.cms
    })
  ]
})
export class AppModule {}

The module is global: true — inject via SYSTEM_CORE_TOKEN anywhere:

ts
import { Inject, Injectable } from '@nestjs/common'
import { SYSTEM_CORE_TOKEN } from '@system-core/core/integrations/nestjs'
import type { SystemCoreProviders } from '@system-core/core/integrations/nestjs'

@Injectable()
export class PagesService {
  constructor(
    @Inject(SYSTEM_CORE_TOKEN) private readonly core: SystemCoreProviders
  ) {}

  listPublished() {
    return (this.core.cms as ReturnType<typeof system.cms>).listPublishedPages()
  }
}

Guards

Auth Guard

ts
import { UseGuards, Controller, Get } from '@nestjs/common'
import { createAuthGuard } from '@system-core/core/integrations/nestjs'

const AuthGuard = createAuthGuard(system.auth)

@Controller('me')
@UseGuards(AuthGuard)
export class MeController {
  @Get()
  getMe() { return 'authorized' }
}

createAuthGuard reads the Authorization: Bearer <token> header, verifies it through auth.sessions.verifyToken, and attaches the session to request.user. Throws 401 if the token is missing or invalid.

Permission Guard

Chain a permission guard after the auth guard:

ts
import { createPermissionGuard } from '@system-core/core/integrations/nestjs'

const CanPublishGuard = createPermissionGuard(
  'cms:publish',
  (userId, permission) =>
    system.auth.authorization.hasEffectivePermission(userId, permission)
)

@UseGuards(AuthGuard, CanPublishGuard)
@Post('pages/:id/publish')
async publishPage(@Param('id') id: string) { ... }

Using the Adapter Directly in Controllers

When you need raw access to request data inside a handler:

ts
import { Controller, Get, ExecutionContext } from '@nestjs/common'
import { getNestjsEvent, nestjsAdapter } from '@system-core/core/integrations/nestjs'

@Controller('echo')
export class EchoController {
  @Get()
  async echo(@ExecutionCtx() ctx: ExecutionContext) {
    const event  = getNestjsEvent(ctx)
    const method = nestjsAdapter.getMethod(event)
    const url    = nestjsAdapter.getRequestURL(event)
    return { method, url: url.pathname }
  }
}

Exception Filter

Map system-core errors to NestJS HTTP responses:

ts
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'
import { isSystemCoreError } from '@system-core/core/integrations/nestjs'

@Catch()
export class SystemCoreExceptionFilter implements ExceptionFilter {
  catch(error: unknown, host: ArgumentsHost) {
    const ctx      = host.switchToHttp()
    const response = ctx.getResponse()

    if (isSystemCoreError(error)) {
      response.status(error.statusCode).json({ message: error.message })
    } else {
      response.status(500).json({ message: 'Internal server error' })
    }
  }
}

Register globally in main.ts:

ts
app.useGlobalFilters(new SystemCoreExceptionFilter())

Platform Compatibility

The adapter detects the underlying platform from the response shape and works with both:

PlatformCookie API used
Express (default)res.cookie() / res.clearCookie()
Fastifyres.setCookie() / @fastify/cookie
Either (fallback)Raw Set-Cookie header

No configuration needed — the adapter checks which methods are present at runtime.

What Stays Unchanged

All other framework adapters — Nuxt/H3 (integrations/nuxt) and Express/Fastify (integrations/express) — are not affected. This adapter is purely additive.

API Reference

ExportKindDescription
nestjsAdapter / httpAdapterHttpAdapterThe core adapter instance
getNestjsEvent(ctx)functionConvert ExecutionContextNestjsHttpContext
createAuthGuard(auth)functionFactory returning a Bearer token Guard class
createPermissionGuard(perm, fn)functionFactory returning a permission Guard class
SystemCoreModuleobjectDynamic module with .forRoot(config)
SYSTEM_CORE_TOKENconstDI injection token string
isSystemCoreError(err)functionType guard for exception filters
NestjsHttpContexttype{ request, response } shape
SystemCoreProviderstypeProviders registered by forRoot

system-core documentation for maintainers, integrators, and AI build agents.