Project structure
Application bootstrap, main modules, and entry point.This guide describes how the NestJS application is initialized and how modules connect.
Entry point
The src/host/main.ts file configures OpenAPI documentation, global error filter, and starts the server. CORS, cookies, and rate limit live in applyHttpMiddleware (src/host/bootstrap/). Details: HTTP middleware. Core projects (without auth/cron) stay slim — the CLI removes optional imports and blocks when features were not selected.
import { applyHttpMiddleware } from '@/host/bootstrap/apply-http-middleware';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { defineDocumentation } from './open-api/define-documentation';
import { ErrorsFilter } from './filters/errors.filter';
import { HttpAdapterHost } from '@nestjs/core';
import { ILoggingService } from '@/domain/common/ilogging.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
applyHttpMiddleware(app);
await defineDocumentation(app);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new ErrorsFilter(httpAdapter, app.get(ILoggingService)));
await app.listen(process.env.PORT || 3000);
}
bootstrap();
With authentication (kl-nest add auth or selected in new): global guards (AuthGuard, ProfilesGuard). Cookies are already handled by applyHttpMiddleware.
With cron/event jobs (kl-nest add cron / add events): infrastructure under src/core/background-services/ and JobsModule.register() in AppModule (empty arrays in Default; sample handlers in CRUD).
Root module
AppModule imports environment validation and feature modules. In the CRUD Example template, PersonModule is already registered; in the Default template, you add modules as you create resources.
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validate: (config) => envSchema.parse(config),
}),
JobsModule.register({
eventHandlers: [], // or sample handlers in CRUD template
cronJobs: [],
}),
PersonModule, // CRUD template
],
})
export class AppModule {}
AuthModule, SecurityModule, and global guards. In the Default template, auth is optional. See Authentication.
Background jobs
Every project includes src/host/jobs/ with JobsModule.register(). Pass handler classes in eventHandlers and cronJobs; JobsBootstrapService subscribes to events and starts cron jobs on OnModuleInit (controlled by CRON_JOBS_ENABLED).
JobsModule.register({
eventHandlers: [InactivePersonHandler],
cronJobs: [CreatePersonJob, DeleteInactiveJob],
})
In the example template, CRON_JOBS_ENABLED=true in .env.example. Tune BOOTSTRAP_DELAY_MS if dependencies need warm-up before jobs. See Cron and Event Jobs.
In the application module, organize jobs by type and specialty:
src/application/<resource>/jobs/
├── cron/ # CronJobHandlerBase
│ └── my-job.ts
└── events/
└── <resource>/ # EventJob aggregate
├── <resource>-event.job.ts
└── <specialty>/ # event + handler
├── my-event.event.ts
└── my-event.handler.ts
Person example: jobs/cron/create-person.job.ts and jobs/events/person/inactive-person/.
Module hierarchy
AppModule
├── ConfigModule (Zod env)
├── HealthCheckModule # opt-in: kl-nest add health
├── SecurityModule # opt-in: auth
├── AuthModule # opt-in: auth
└── PersonModule # CRUD template
└── ControllerModule
├── MappingProvider
└── InfraModule
├── ILoggingService
├── ICacheService # opt-in: cache / OAuth2 / cron
├── IRedLockService # opt-in: cache with cron
└── RepositoryModule
└── DatabaseModule
MappingProvider ensures all mappings are registered before any handler runs:
@Injectable()
export class MappingProvider {
constructor() {
PersonMapper.createMap();
}
}
Import alias
Generated projects use the @/ alias pointing to src/. Real import example:
import { Person } from '@/domain/entities/person/person';
import { AutoMapper } from '@/core/tools/mapping';
import { IPersonRepository } from '@/domain/repositories/iperson.repository';
Useful scripts
Bun
bun run start:dev
bun run start:prod
bun test
bun test --watch
bun run migration:generate
bun run migration:run
bun run migration:revert
npm / pnpm — use npm run or pnpm run with the same script names. Tests use Vitest; migration:generate, migration:run, and migration:revert use node --import ts-node/register/transpile-only.