NestJS (`nestjs`)
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/shopComponent spec
| Option | gnx create flag | Default | Notes |
|---|---|---|---|
nodeVersion | --node | 22 | Node.js major version |
packageManager | --package-manager | npm | npm, pnpm or yarn |
basePath | from the contract | Path prefix of the API; otherwise taken from servers[0].url | |
exposes | --port, --contract | port 8080 | Ports: see Ports, Middleware & Security; exposes: [] = no HTTP |
exposes[].middleware, .validation, .security | Middleware, validation, authentication | ||
modules | Security 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 testWriting 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.tsand pass the ones the services need toGeneratedApiModule.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 withcodeandmessage;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()fromsrc/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:devNot generated yet
- String lengths, patterns and number ranges in the DTOs
- Databases, REST clients, Redis and messaging modules
apiKeyandbasicsecurity schemes, Keycloak incompose.yaml