generating-nest-servers

Installation
SKILL.md

NestJS Server Development Expert

Gotchas

  • declare keyword on Model properties kills decorators — declare name: string tells TypeScript "this property exists but isn't emitted at runtime" — which means Mongoose/Typegoose decorators (@Prop, @Field) attached to it are lost. Always use name!: string (definite assignment) or name: string = '' (default value) on model fields. Tests pass silently in memory but production MongoDB writes drop the field.
  • securityCheck() returning this unchanged is a red flag — A trivial return this; implementation means no permission boundary exists on the model. The decorator system requires @Restricted/@Roles + an actual check (usually filtering sensitive fields or verifying ownership). If the model genuinely has nothing to restrict, add a comment explaining why — otherwise it will be flagged in every security review.
  • Code that extends the framework follows four NestJS 12 rules — (1) A Core service override that declares its own constructor marks each optional parameter @Optional() itself: NestJS 12 reads the flags from the class whose constructor runs, so an unmarked mapper?: X becomes required, and where nothing provides it the boot fails with Nest can't resolve dependencies of the XService (…, ?). A subclass without a constructor of its own keeps the parent's flags. (2) Import @nestjs/* symbols and types from the package root (import type { JwtSignOptions } from '@nestjs/jwt'), never from …/dist/…: @nestjs/{jwt,mongoose,passport,schedule,swagger,terminus} export only their root, so a deep path fails with TS2307 or ERR_PACKAGE_PATH_NOT_EXPORTED. A type the root does not export is derived from one it does (NonNullable<ApiPropertyOptions['enum']>). (3) A GraphQL upload argument uses import { GraphQLUpload } from '@lenne.tech/nest-server', never graphql-upload/GraphQLUpload.js: the scalar accepts only Upload instances of its own copy, the multipart middleware is nest-server's, and with two copies every upload fails with Upload value invalid.. (4) No relative dynamic import() in source: no specifier works everywhere (tsc output needs ./x.js, a ts-node source run does not map it to .ts); use a static import, or move what a cycle needs into a module of its own.
  • An ESM project ("type": "module" in the API's package.json) needs five more rules, and its tests do not enforce the worst one — nest-server is an ES module from 12.0 on; a CommonJS project keeps writing code as before. In an ESM project: (1) every relative import names the emitted file, ./x.js or ./dir/index.js (current lt server generators write it there); (2) a JSON import carries with { type: 'json' }, and __dirname becomes import.meta.dirname; (3) an interface or type alias in a decorated signature is imported with import type (TS1272 otherwise), never a class whose type drives dependency injection; (4) a CommonJS package whose names Node's analysis misses takes a type-only or default import: import { Connection } from 'mongoose' passes every vitest test and fails the production start with does not provide an export named 'Connection', so start the built server (check:server-start) after such an import; (5) a decorated property or constructor parameter typed by a class from an import cycle (two models referencing each other) is typed WrapperType<X> (nest-server export), otherwise the start throws Cannot access 'X' before initialization. Converting a project is its own step (lt server convert-esm), never a side effect of a feature (nest-server-updating).
  • --controller flag generates REST — even in GraphQL projects — lt server object <name> --controller produces a REST controller by default. For GraphQL projects, use --resolver instead. The CLI does not auto-detect from the project's existing pattern.
  • CrudService is the default — always extend it — New modules often get hand-rolled service classes instead of extends CrudService. The framework's pagination, filtering, population, and permission integration all depend on CrudService inheritance. Hand-rolled services break /api/<entity>/find queries silently for consumers.
  • Module models extend the project's PersistenceModel, objects extend CoreModel — lt server module generates export class X extends PersistenceModel with import { PersistenceModel } from '../../common/models/persistence.model' (the project base, itself extends CorePersistenceModel), which carries id, timestamps and createdBy/updatedBy. lt server object generates extends CoreModel for embedded, non-persisted types. A module model on CoreModel loses those persistence fields.
  • Alphabetical property order is enforced in reviews — Class fields, DTO properties, and model definitions are reviewed for alphabetical ordering. Generated code from lt server usually respects this; hand-edited additions often break it. Run a final pass before committing.

Ecosystem Context

Developers typically work in a Lerna fullstack monorepo created via lt fullstack init:

Installs
1
First Seen
Oct 27, 2026