Skip to Content
Genexy IDE Beta 0.9.1 · Download the Beta build
Features · Code generation

NestJS (`nestjs`)

PROIDEExperimental

Generates a NestJS 11 service in TypeScript on the Express platform: controllers, DTO classes with class-validator rules and one abstract service per OpenAPI tag, which you implement.

The ports, their middleware and JWT / OIDC are the same runtime as the Node.js generator (platform.ts): one Nest application, served on one node:http server per port. A global guard checks the ports, the security of the contract and unknown body properties; a global filter answers every error as a problem detail.

Quick start

gnx create orders --type nestjs --dir services/orders --node 22 --contract services/orders/api/orders.openapi.yaml --port 8080
# services/orders/.genexy/component.yaml type: nestjs nodeVersion: '22' packageManager: npm exposes: - name: api port: 8080 contracts: - $ref: api/orders.openapi.yaml middleware: cors: { allowedOrigins: ['https://shop.example.com'] } security: auth modules: - name: auth type: security provider: oidc issuer: https://sso.example.com/realms/shop

Component spec

Optiongnx create flagDefaultNotes
nodeVersion--node22Node.js major version
packageManager--package-managernpmnpm, pnpm or yarn
basePathfrom the contractPath prefix of the API; otherwise taken from servers[0].url
exposes--port, --contractport 8080Ports: see Ports, Middleware & Security; exposes: [] = no HTTP
exposes[].middleware, .validation, .securityMiddleware, validation, authentication
modulesSecurity modules (type: security); databases and clients are not generated yet

Contracts: OpenAPI 3.0 and 3.1. OpenAPI support: allOf, oneOf and anyOf (as union types), enums, nullable, additionalProperties, shared parameters, request bodies and responses; one service per first tag, the method named after operationId.

What is generated

orders/ ├─ .genexy/component.yaml the component spec (and generated.json) ├─ package.json, tsconfig*.json, nest-cli.json yours (created once) ├─ Dockerfile, .env.example, README.md yours ├─ src/ │ ├─ main.ts generated: setupApp, configureApp, startPorts │ ├─ app.module.ts stub: your modules │ ├─ app.config.ts stub: your middleware, Swagger and so on │ ├─ service/orders.service.impl.ts stub, one per OpenAPI tag │ └─ generated/ generated │ ├─ controllers/, services/ controllers (with @Operation) and the abstract services │ ├─ models/ DTO classes with validation rules │ ├─ setup.ts setupApp() and startPorts() │ ├─ security.ts the operation guard and the problem detail filter │ ├─ platform.ts, settings.ts, shapes.ts the port runtime, the settings of the spec, the schemas │ └─ http-error.ts, generated.module.ts └─ test/app.e2e-spec.ts stub: Jest end-to-end test

Writing the business logic

import { Injectable } from '@nestjs/common'; import { forbidden, notFound } from '../generated/http-error'; import { currentPrincipal } from '../generated/platform'; import { OrdersService } from '../generated/services/orders.service'; import type { Order } from '../generated/models'; @Injectable() export class OrdersServiceImpl extends OrdersService { async getOrder(orderId: number): Promise<Order> { const order = await this.orders.find(orderId); if (!order) throw notFound('order ' + orderId + ' not found'); const caller = currentPrincipal(); if (caller && !caller.hasRole('admin') && order.owner !== caller.subject) throw forbidden('not your order'); return order; } }
  • Dependencies: register your own modules in src/app.module.ts and pass the ones the services need to GeneratedApiModule.forRoot({ imports }); inject them in the constructor.
  • Errors: the helpers of src/generated/http-error (httpError(status, message), notFound, conflict, forbidden…) are answered as problem details with code and message; httpError(status, message, body) sends the error model of the contract exactly.
  • Validation: request bodies are validated against the DTO rules, parameters are converted to their types, and unknown body properties are rejected when the port asks for it.
  • The caller: currentPrincipal() from src/generated/platform; undefined for an anonymous request.
⚠️

The DTOs do not check string lengths, patterns and number ranges of the contract yet; the other generators answer such requests with 400.

Ports, middleware and security

startPorts(app) initializes the application and starts one server per port, each with the middleware of the port. A request for an operation of a contract the port does not serve answers 404. All options: Ports, Middleware & Security.

In end-to-end tests, set the application up like main.ts does and start the ports on free numbers:

const moduleRef = await Test.createTestingModule({ imports: [AppModule] }).compile(); const app = moduleRef.createNestApplication(); setupApp(app); const running = await startPorts(app, { ports: { api: 0 }, host: '127.0.0.1', security: { auth: { jwksUrl } } }); const res = await fetch(running.ports.url('api') + '/api/v1/orders/1'); await running.close();

app.getHttpServer() with supertest still works, without the port middleware; operations a secured port protects then answer 401.

Build and run

Needs Node.js and the chosen package manager. In the component folder:

npm install npm run build npm test npm run start:dev

Not generated yet

  • String lengths, patterns and number ranges in the DTOs
  • Databases, REST clients, Redis and messaging modules
  • apiKey and basic security schemes, Keycloak in compose.yaml