Skip to Content

NestJS

The NestJS adapter wires the engine through DI with a LunoModule and a LUNO injection token, so the engine composes with the rest of your container instead of sitting beside it.

Install

npm install @luno-oss/core @luno-oss/nestjs

Register the module

forRoot binds an engine you built:

import { Module } from '@nestjs/common' import { LunoModule } from '@luno-oss/nestjs' import { createLuno, memoryStore } from '@luno-oss/core' @Module({ imports: [ LunoModule.forRoot({ luno: createLuno({ store: memoryStore(), secret: process.env.LUNO_SECRET! }) }) ] }) export class AppModule {}

forRootAsync builds one from a factory, so it can inject ConfigService:

LunoModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => createLuno({ store: postgresStore(pool), secret: config.getOrThrow('LUNO_SECRET') }) })

Either way the enrol controller is registered and LUNO is exported for your own providers.

Attach the socket

The socket goes onto Nest’s underlying HTTP server, separately from the module:

import { NestFactory } from '@nestjs/core' import { LUNO, attachLunoWebSocket, type Luno } from '@luno-oss/nestjs' const app = await NestFactory.create(AppModule) attachLunoWebSocket(app.getHttpServer(), app.get<Luno>(LUNO)) await app.listen(3000)

This is deliberately not a @WebSocketGateway. The Luno protocol is raw frames, not the socket.io event shape a gateway imposes — bridging the raw server is both simpler and correct here.

Inject the engine

import { Inject, Injectable } from '@nestjs/common' import { LUNO, type Luno } from '@luno-oss/nestjs' @Injectable() export class AlertService { constructor(@Inject(LUNO) private readonly luno: Luno) {} notify(deviceId: string, to: string) { return this.luno.sms.send({ deviceId, to, body: 'Alert' }) } }

Guard your own controllers. The module exposes only the node-facing enrol routes; anything that reaches luno.sms.send is yours to authenticate. See the two axes.

API

ExportPurpose
LunoModule.forRoot({ luno })Registers the engine and enrol controller
LunoModule.forRootAsync({ useFactory, inject?, imports? })The same, built from a factory
LUNOThe injection token for Luno
attachLunoWebSocket(server, luno, options?)Attaches WS /ws

Next

  • Express — the same socket bridge, unwrapped
  • Core API — what you get on the injected engine
  • Testing — run a fake node against it