Skip to main content

NestJS backend

Ness uses NestJS for public server routes while React Router handles the UI, loaders, actions, and SSR. Both frameworks run on the same HTTP server: Nest controllers are mounted first and unmatched requests continue to the React application.

Project structure​

app/
β”œβ”€β”€ routes/ # React pages, layouts, loaders, actions
└── server/ # NestJS backend
β”œβ”€β”€ app.module.ts
└── users/
β”œβ”€β”€ users.controller.ts
β”œβ”€β”€ users.service.ts
└── users.module.ts

New applications include @nessframework/nest, NestJS 11, reflect-metadata, and RxJS. NestJS 11 and Express 5 must be used together: @nestjs/platform-express@11 calls Express 5 APIs, and pairing it with Express 4 fails at application start.

app/server is TypeScript in every starter, including the JavaScript one. Nest is built on decorators, and decorators do not exist in JavaScript β€” writing this folder in JavaScript means applying every decorator by hand:

// What JavaScript forces you to write instead of @Get('health')
Get('health')(
ApiController.prototype,
'health',
Object.getOwnPropertyDescriptor(ApiController.prototype, 'health'),
);

The JavaScript starter therefore keeps app/server/tsconfig.json scoped to that folder rather than at the project root, so the React side stays JavaScript. Nothing else needs configuring: the Nest compiler transpiles app/server on its own, in development and in the build.

Root module​

app/server/app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module.js';

@Module({ imports: [UsersModule] })
export class AppModule {}

Use .js in relative TypeScript imports. The Nest compiler resolves the source .ts file and emits ESM files that Node can load directly.

Controller and service​

app/server/users/users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service.js';

@Controller('users')
export class UsersController {
constructor(private readonly users: UsersService) {}

@Get()
findAll() {
return this.users.findAll();
}
}
app/server/users/users.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
findAll() {
return [];
}
}

All controllers receive the global /api prefix by default. Nest decorators for validation, guards, interceptors, exception filters, versioning, and OpenAPI can be used normally.

Generate backend modules​

ness g controller users
ness g service users
ness g module users
ness g guard users/auth
ness g resource products

The generator writes Nest modules under app/server and registers controllers, providers, guards, and imported modules in app/server/app.module.ts when it exists.

Development and production​

The @nessframework/nest Vite plugin compiles decorators with emitDecoratorMetadata, mounts the application during ness dev, and reloads it when app/server changes. ness build writes the ESM backend to build/nest.

The production bridge is configured in ness.config.mjs:

import { defineNessConfig } from '@nessframework/router';
import { nestServer } from '@nessframework/nest/server';

export default defineNessConfig({
server: { configureServer: nestServer({ prefix: 'api' }) },
});

The prefix must be non-empty so unknown URLs can continue to the React application. Change it to another namespace such as v1 when needed.