NestJS — Enterprise Node.js

Bootstrapping & Project Structure

14 min Lesson 3 of 48

Bootstrapping & Project Structure

A freshly generated NestJS project has a predictable layout. Understanding how the app boots — and what each generated file does — makes everything that follows easier. Every NestJS application starts from a single entry file and a single root module.

The entry point: main.ts

src/main.ts is where the application is created and starts listening. It uses the NestFactory to build an application instance from the root module:

// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();

NestFactory.create() takes the root module and returns a fully wired application: it reads every module, instantiates providers, resolves dependencies, and registers controllers. app.listen(3000) then starts the HTTP server.

main.ts is also where global setup lives — global pipes, CORS, a route prefix, Swagger, and more are configured on the app instance here, before listen().

The root module: app.module.ts

The root module is the tree's trunk. Every other module is imported into it (directly or indirectly):

// src/app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [], controllers: [AppController], providers: [AppService], }) export class AppModule {}

The @Module() decorator takes a metadata object with four possible keys:

  • imports — other modules whose exported providers this module needs.
  • controllers — controllers instantiated by this module.
  • providers — services available for injection inside this module.
  • exports — providers this module shares with modules that import it.

Standard folder layout

src/ main.ts # entry point — bootstraps the app app.module.ts # root module app.controller.ts # example controller app.service.ts # example service test/ # end-to-end tests nest-cli.json # CLI configuration tsconfig.json # TypeScript configuration package.json # dependencies and scripts
Convention: group code by feature, not by type. A users feature gets its own folder containing users.module.ts, users.controller.ts, users.service.ts, and a dto/ folder — everything about users in one place.

The file-naming convention

NestJS names files name.type.ts: users.controller.ts, users.service.ts, users.module.ts. This is not cosmetic — the CLI relies on these suffixes when generating and wiring files, and it makes large projects instantly navigable.

Every provider must be registered. If you create a service but forget to add it to a module's providers array, NestJS cannot inject it and throws a "Nest can't resolve dependencies" error at startup. Using nest g service avoids this by registering it for you.

Summary

A NestJS app boots from main.ts, which uses NestFactory.create() to build the app from the root AppModule. Modules declare their controllers and providers via the @Module() decorator, and code is organised by feature using the name.type.ts convention. Next we will dive into controllers — the entry point for every request.