Skip to main content

NestJS Project Setup: Creating Your First Application

You've learned what NestJS is and why it's powerful. Now comes the exciting part - let's build something! By the end of this guide, you'll have a running NestJS application and understand every file in your project.

Think of this as unpacking a new toolbox. We'll explore each tool, understand what it does, and use it to build something real.

Quick Reference

Setup command:

npm i -g @nestjs/cli
nest new my-project
cd my-project
npm run start:dev

Project structure:

src/
main.ts # Application entry point
app.module.ts # Root module
app.controller.ts # Sample controller
app.service.ts # Sample service

Common commands:

  • nest new <name> - Create new project
  • nest generate <schematic> - Generate code
  • npm run start:dev - Run with hot reload
  • npm run build - Build for production

Gotchas:

  • ⚠️ CLI installation requires Node.js 18+
  • ⚠️ Project names can't contain spaces or special characters
  • ⚠️ Always use npm run start:dev during development

What You Need to Know First

Required reading:

Technical prerequisites:

  • Node.js installed: Version 18.x or higher
  • npm basics: How to install packages and run scripts
  • Terminal/Command line: Basic navigation (cd, ls/dir)
  • Text editor/IDE: VS Code recommended (with TypeScript support)

System requirements:

  • 4 GB RAM minimum (8 GB recommended)
  • 1 GB free disk space
  • macOS, Windows, or Linux

What We'll Cover in This Article

By the end of this guide, you'll understand:

  • How to install the NestJS CLI globally
  • How to create a new NestJS project
  • The complete project structure and what each file does
  • How to run your application in development mode
  • How to generate new components using the CLI
  • Common CLI commands and their purposes

What We'll Explain Along the Way

These concepts will be explained with examples as we encounter them:

  • Project scaffolding (what files are generated and why)
  • Package.json scripts (what each command does)
  • TypeScript configuration (tsconfig.json explained)
  • Hot reload and watch mode (how it works)
  • Development vs production builds

Installing the NestJS CLI

The NestJS Command Line Interface (CLI) is your best friend when working with NestJS. It's like having a construction crew that builds the framework of your application for you.

What Is the CLI?

The CLI is a tool that:

  • Creates new projects with proper structure
  • Generates boilerplate code (controllers, services, modules)
  • Manages your project's build and development
  • Saves hours of manual file creation

Think of it like a blueprint machine - you tell it what you want, and it creates the perfect structure for you.

Step 1: Verify Node.js Installation

Before installing the CLI, let's make sure you have Node.js installed:

# Check Node.js version
node --version
# Should output: v18.x.x or higher

# Check npm version
npm --version
# Should output: 9.x.x or higher

What you'll see:

$ node --version
v20.10.0

$ npm --version
10.2.3

If you see "command not found":

  • You need to install Node.js first
  • Download from: https://nodejs.org/
  • Choose the LTS (Long Term Support) version
  • Install and restart your terminal

Step 2: Install NestJS CLI Globally

Now let's install the CLI so it's available from anywhere on your system:

# Install globally with npm
npm install -g @nestjs/cli

# Or if you prefer using npx (no installation needed)
# npx @nestjs/cli new my-project

What's happening here:

  1. npm install: The npm package installer
  2. -g: Global flag - installs for your entire system, not just one project
  3. @nestjs/cli: The NestJS command-line interface package

Installation output:

$ npm install -g @nestjs/cli

added 276 packages in 23s

22 packages are looking for funding
run `npm fund` for details

This might take 1-2 minutes. The CLI is installing itself and all its dependencies.

Step 3: Verify CLI Installation

Let's confirm the CLI is installed correctly:

# Check if nest command is available
nest --version
# Should output: 10.x.x

# See all available commands
nest --help

What you'll see:

$ nest --version
10.3.0

$ nest --help
Usage: nest <command> [options]

Options:
-v, --version Output the current version.
-h, --help Output usage information.

Commands:
new|n [options] [name] Generate Nest application.
generate|g [options] <schematic> Generate a Nest element.
build [options] [app] Build Nest application.
start [options] [app] Run Nest application.
info|i Display Nest project details.

If you see "command not found":

  1. Close and reopen your terminal (necessary for global installations)
  2. Check npm global path:
    npm config get prefix
    # Make sure this path is in your system PATH
  3. Alternative: Use npx (no installation required):
    npx @nestjs/cli --version

Understanding the CLI Commands

Here's what each command does - we'll use these throughout your NestJS journey:

# Create new project
nest new <project-name>

# Generate components
nest generate controller users
nest generate service users
nest generate module users

# Build and run
nest build # Compile TypeScript to JavaScript
nest start # Run in production mode
nest start --watch # Run in development mode with hot reload

# Get project information
nest info # Shows versions and environment details

Real-world analogy:

  • nest new = Building a new house (complete structure)
  • nest generate = Adding new rooms (controllers, services)
  • nest start = Opening the house for business
  • nest build = Final inspection before opening

Creating Your First NestJS Project

Now that we have the CLI installed, let's create our first project. This is where the magic happens!

Step 1: Create a New Project

# Create a project called "my-first-nest-app"
nest new my-first-nest-app

# You'll see interactive prompts - let's walk through them

What you'll see:

$ nest new my-first-nest-app

⚡ We will scaffold your app in a few seconds..

? Which package manager would you ❤️ to use? (Use arrow keys)
npm
yarn
pnpm

Choose your package manager:

  • npm: Default, comes with Node.js (recommended for beginners)
  • yarn: Alternative, faster in some cases
  • pnpm: Disk space efficient, faster installs

For this guide, let's choose npm (press Enter).

Step 2: Watch the Project Creation

After selecting npm, you'll see:

CREATE my-first-nest-app/.eslintrc.js (663 bytes)
CREATE my-first-nest-app/.prettierrc (51 bytes)
CREATE my-first-nest-app/README.md (3339 bytes)
CREATE my-first-nest-app/nest-cli.json (171 bytes)
CREATE my-first-nest-app/package.json (1951 bytes)
CREATE my-first-nest-app/tsconfig.build.json (97 bytes)
CREATE my-first-nest-app/tsconfig.json (546 bytes)
CREATE my-first-nest-app/src/app.controller.spec.ts (617 bytes)
CREATE my-first-nest-app/src/app.controller.ts (274 bytes)
CREATE my-first-nest-app/src/app.module.ts (249 bytes)
CREATE my-first-nest-app/src/app.service.ts (142 bytes)
CREATE my-first-nest-app/src/main.ts (208 bytes)
CREATE my-first-nest-app/test/app.e2e-spec.ts (630 bytes)
CREATE my-first-nest-app/test/jest-e2e.json (183 bytes)

✔ Installation in progress... ☕

🚀 Successfully created project my-first-nest-app
👉 Get started with the following commands:

$ cd my-first-nest-app
$ npm run start

What just happened?

  1. Created project folder with all necessary files
  2. Generated starter code (controllers, services, modules)
  3. Installed dependencies (Express, TypeScript, testing tools)
  4. Set up configuration (TypeScript, ESLint, Prettier)

This took about 30-60 seconds. The CLI just saved you hours of manual setup!

Step 3: Navigate to Your Project

# Enter your project directory
cd my-first-nest-app

# List the files
ls -la
# On Windows: dir

What you'll see:

$ cd my-first-nest-app
$ ls -la

drwxr-xr-x node_modules/ # Dependencies (1000+ packages)
drwxr-xr-x src/ # Your application code
drwxr-xr-x test/ # End-to-end tests
-rw-r--r-- .eslintrc.js # Code quality rules
-rw-r--r-- .prettierrc # Code formatting rules
-rw-r--r-- nest-cli.json # NestJS CLI configuration
-rw-r--r-- package.json # Project dependencies
-rw-r--r-- README.md # Project documentation
-rw-r--r-- tsconfig.json # TypeScript configuration

Congratulations! You've just created your first NestJS project. Let's explore what each file does.

Understanding the Project Structure

Let's take a tour of your new project, file by file. Think of this as a guided tour of your new home - we'll visit every room and understand its purpose.

The Root Directory: Project Configuration

my-first-nest-app/
├── node_modules/ # Dependencies (don't edit)
├── src/ # Your application code (THIS IS WHERE YOU WORK)
├── test/ # End-to-end tests
├── .eslintrc.js # Linting rules
├── .prettierrc # Formatting rules
├── nest-cli.json # NestJS CLI config
├── package.json # Project metadata & scripts
├── tsconfig.json # TypeScript configuration
├── tsconfig.build.json # Build-specific TS config
└── README.md # Project documentation

The src/ Directory: Your Application Code

This is where you'll spend 99% of your time:

src/
├── main.ts # Application entry point (starts the server)
├── app.module.ts # Root module (organizes everything)
├── app.controller.ts # Sample HTTP controller
├── app.service.ts # Sample business logic service
└── app.controller.spec.ts # Unit tests for controller

Let's explore each file in detail:

1. main.ts - The Application Entry Point

This is where your application starts. Think of it as the ignition key of a car:

// src/main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
// Create the NestJS application
const app = await NestFactory.create(AppModule);

// Start listening on port 3000
await app.listen(3000);
}

// Start the application
bootstrap();

What's happening here:

// Step 1: Import the necessary pieces
import { NestFactory } from "@nestjs/core";
// NestFactory is like a car factory - it creates your application

import { AppModule } from "./app.module";
// AppModule is the blueprint for what to build

// Step 2: Define the startup function
async function bootstrap() {
// "bootstrap" is a common term meaning "start up"

// Step 3: Create the application
const app = await NestFactory.create(AppModule);
// This line:
// 1. Reads your AppModule blueprint
// 2. Creates all controllers and services
// 3. Sets up dependency injection
// 4. Configures Express/Fastify
// All in this one line!

// Step 4: Start the server
await app.listen(3000);
// Server is now running on http://localhost:3000

console.log("Application is running on: http://localhost:3000");
}

// Step 5: Execute the startup function
bootstrap();

Customizing main.ts:

You'll often enhance this file as your app grows:

// Enhanced main.ts with common additions
import { NestFactory } from "@nestjs/core";
import { ValidationPipe } from "@nestjs/common";
import { AppModule } from "./app.module";

async function bootstrap() {
const app = await NestFactory.create(AppModule);

// Enable CORS for frontend applications
app.enableCors();

// Enable validation for all endpoints
app.useGlobalPipes(new ValidationPipe());

// Set global prefix for all routes
app.setGlobalPrefix("api"); // All routes now start with /api

// Start server
const port = process.env.PORT || 3000;
await app.listen(port);

console.log(`Application is running on: http://localhost:${port}`);
}

bootstrap();

2. app.module.ts - The Root Module

Modules are like departments in a company. The root module is the headquarters:

// src/app.module.ts
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

@Module({
imports: [], // Other modules this one needs
controllers: [AppController], // HTTP handlers
providers: [AppService], // Business logic services
})
export class AppModule {}

Understanding the @Module decorator:

@Module({
// 1. imports: Other modules we depend on
imports: [
// Example: UsersModule, ProductsModule
// We'll add these as we build features
],

// 2. controllers: Classes that handle HTTP requests
controllers: [
AppController, // Handles routes like GET /
],

// 3. providers: Services that can be injected
providers: [
AppService, // Business logic used by controllers
],

// 4. exports: What other modules can use (optional)
exports: [
// AppService, // If other modules need it
],
})
export class AppModule {}

Real-world example as your app grows:

// app.module.ts - After adding features
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { UsersModule } from "./users/users.module";
import { ProductsModule } from "./products/products.module";
import { AuthModule } from "./auth/auth.module";

@Module({
imports: [
UsersModule, // User management feature
ProductsModule, // Product catalog feature
AuthModule, // Authentication feature
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Key insight: The root module imports all feature modules. It's like a table of contents for your entire application.

3. app.controller.ts - Handling HTTP Requests

Controllers are like receptionists - they receive requests and return responses:

// src/app.controller.ts
import { Controller, Get } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller() // Base route: / (empty = root)
export class AppController {
// Dependency injection: service is automatically provided
constructor(private readonly appService: AppService) {}

@Get() // Handles GET /
getHello(): string {
// Delegate to service for business logic
return this.appService.getHello();
}
}

Breaking down the controller:

// 1. Import necessary decorators
import { Controller, Get } from "@nestjs/common";
import { AppService } from "./app.service";

// 2. Mark class as a controller
@Controller() // Could be @Controller('users') for /users routes
export class AppController {
// 3. Inject dependencies via constructor
constructor(
private readonly appService: AppService // "private readonly" creates a property and assigns it automatically // Now we can use this.appService anywhere in the class
) {}

// 4. Define route handlers
@Get() // Responds to GET requests to /
getHello(): string {
// Return type is string
return this.appService.getHello();
// Controller stays thin - delegates to service
}

// We can add more routes:
@Get("about") // Responds to GET /about
getAbout(): string {
return "About page";
}
}

Controller with more examples:

// Expanded controller showing common patterns
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller("api") // Base route: /api
export class AppController {
constructor(private readonly appService: AppService) {}

// GET /api
@Get()
getHello(): string {
return this.appService.getHello();
}

// GET /api/users
@Get("users")
getUsers(): string[] {
return ["Alice", "Bob", "Charlie"];
}

// GET /api/users/:id
@Get("users/:id")
getUser(@Param("id") id: string): string {
return `User ${id}`;
}

// POST /api/users
@Post("users")
createUser(@Body() userData: any): any {
return { id: 1, ...userData };
}
}

4. app.service.ts - Business Logic

Services contain your actual business logic. Controllers call services:

// src/app.service.ts
import { Injectable } from "@nestjs/common";

@Injectable() // Makes this available for dependency injection
export class AppService {
getHello(): string {
return "Hello World!";
}
}

Understanding services:

// 1. Mark as injectable
@Injectable() // This decorator is REQUIRED
export class AppService {
// 2. Business logic methods
getHello(): string {
// Simple method returning a string
return "Hello World!";
}

// Services can have multiple methods
getWelcomeMessage(name: string): string {
return `Welcome, ${name}!`;
}

// Services can be async
async fetchData(): Promise<any> {
// Imagine calling a database or API here
return { data: "some data" };
}
}

Why separate services from controllers?

// ❌ Bad: Business logic in controller
@Controller()
export class AppController {
@Get("users/:id")
getUser(@Param("id") id: string) {
// All logic in controller - hard to test and reuse
const user = database.query(`SELECT * FROM users WHERE id = ${id}`);
if (!user) throw new Error("Not found");
delete user.password;
return user;
}
}

// ✅ Good: Business logic in service
@Injectable()
export class UsersService {
async findById(id: string): Promise<User> {
const user = await this.database.findOne({ where: { id } });
if (!user) throw new NotFoundException("User not found");
return this.sanitizeUser(user);
}

private sanitizeUser(user: User): User {
const { password, ...safeUser } = user;
return safeUser;
}
}

@Controller("users")
export class UsersController {
constructor(private usersService: UsersService) {}

@Get(":id")
getUser(@Param("id") id: string) {
// Controller stays thin - just routing
return this.usersService.findById(id);
}
}

Benefits of this separation:

  1. Testable: Test business logic without HTTP layer
  2. Reusable: Use service in multiple controllers
  3. Maintainable: Changes to logic don't affect routing
  4. Clear: Each class has one responsibility

5. app.controller.spec.ts - Unit Tests

NestJS includes testing setup out of the box:

// src/app.controller.spec.ts
import { Test, TestingModule } from "@nestjs/testing";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

describe("AppController", () => {
let appController: AppController;

beforeEach(async () => {
// Create a testing module
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();

// Get instance of controller to test
appController = app.get & lt;
AppController > AppController;
});

describe("root", () => {
it('should return "Hello World!"', () => {
// Test the getHello method
expect(appController.getHello()).toBe("Hello World!");
});
});
});

Understanding the test structure:

// 1. Import testing utilities
import { Test, TestingModule } from "@nestjs/testing";

// 2. Describe what we're testing
describe("AppController", () => {
// Declare variables for test scope
let appController: AppController;

// 3. Setup before each test
beforeEach(async () => {
// Create a mini-application for testing
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController], // What to test
providers: [AppService], // What it depends on
}).compile();

// Get the controller instance
appController = app.get & lt;
AppController > AppController;
});

// 4. Write test cases
describe("root", () => {
it('should return "Hello World!"', () => {
// Call the method
const result = appController.getHello();

// Assert the expected outcome
expect(result).toBe("Hello World!");
});
});
});

We'll cover testing in depth in a future article. For now, know that tests are included by default!

Configuration Files Explained

Let's understand the configuration files in the root directory:

package.json - Project Metadata:

{
"name": "my-first-nest-app",
"version": "0.0.1",
"scripts": {
"build": "nest build", // Compile TypeScript
"start": "nest start", // Run in production mode
"start:dev": "nest start --watch", // Run with hot reload
"start:debug": "nest start --debug --watch", // Run with debugger
"start:prod": "node dist/main", // Run compiled JavaScript
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest", // Run unit tests
"test:watch": "jest --watch", // Run tests in watch mode
"test:cov": "jest --coverage", // Run tests with coverage
"test:e2e": "jest --config ./test/jest-e2e.json" // End-to-end tests
},
"dependencies": {
"@nestjs/common": "^10.0.0", // Core NestJS functionality
"@nestjs/core": "^10.0.0", // NestJS core
"@nestjs/platform-express": "^10.0.0", // Express adapter
"reflect-metadata": "^0.1.13", // Required for decorators
"rxjs": "^7.8.1" // Reactive extensions
},
"devDependencies": {
"@nestjs/cli": "^10.0.0", // CLI tools
"@nestjs/testing": "^10.0.0", // Testing utilities
"typescript": "^5.1.3" // TypeScript compiler
}
}

tsconfig.json - TypeScript Configuration:

{
"compilerOptions": {
"module": "commonjs", // Module system
"declaration": true, // Generate .d.ts files
"removeComments": true, // Remove comments in output
"emitDecoratorMetadata": true, // Required for NestJS
"experimentalDecorators": true, // Enable decorators
"allowSyntheticDefaultImports": true,
"target": "ES2021", // Target JavaScript version
"sourceMap": true, // Generate source maps for debugging
"outDir": "./dist", // Output directory
"baseUrl": "./", // Base directory
"incremental": true, // Faster subsequent builds
"skipLibCheck": true, // Skip type checking of declaration files
"strictNullChecks": false, // Allow null/undefined
"noImplicitAny": false, // Allow implicit any type
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}

nest-cli.json - NestJS CLI Configuration:

{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics", // Code generation templates
"sourceRoot": "src", // Where source code lives
"compilerOptions": {
"deleteOutDir": true // Clean dist/ before building
}
}

Running Your Application

Now for the exciting part - let's see your application in action!

Development Mode (with Hot Reload)

This is what you'll use 99% of the time during development:

# Start the application in watch mode
npm run start:dev

What you'll see:

$ npm run start:dev

[12:34:56 PM] Starting compilation in watch mode...

[12:34:58 PM] Found 0 errors. Watching for file changes.

[Nest] 12345 - 12/01/2024, 12:34:58 PM LOG [NestFactory] Starting Nest application...
[Nest] 12345 - 12/01/2024, 12:34:58 PM LOG [InstanceLoader] AppModule dependencies initialized +15ms
[Nest] 12345 - 12/01/2024, 12:34:58 PM LOG [RoutesResolver] AppController {/}: +3ms
[Nest] 12345 - 12/01/2024, 12:34:58 PM LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 12345 - 12/01/2024, 12:34:58 PM LOG [NestApplication] Nest application successfully started +2ms

What this means:

  1. TypeScript compilation - Your .ts files are compiled to JavaScript
  2. Application startup - NestJS initializes everything
  3. Route mapping - All routes are registered
  4. Server listening - Application is ready on port 3000

Testing Your Application

Open your browser and navigate to:

http://localhost:3000

You should see:

Hello World!

Congratulations! Your first NestJS application is running!

Hot Reload in Action

Let's see the magic of hot reload. Keep the server running and edit a file:

// src/app.service.ts
import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
getHello(): string {
// Change this line:
return "Hello World!";
// To this:
return "Welcome to NestJS! 🚀";
}
}

Save the file and watch your terminal:

[12:36:15 PM] File change detected. Starting incremental compilation...

[12:36:16 PM] Found 0 errors. Compilation successful.

[Nest] 12345 - 12/01/2024, 12:36:16 PM LOG [NestFactory] Starting Nest application...
[Nest] 12345 - 12/01/2024, 12:36:16 PM LOG Application restarted successfully

Refresh your browser - you'll see the new message immediately!

This is hot reload - changes are detected and the server restarts automatically. No need to manually stop and start!

Other Run Modes

# Production mode (no hot reload)
npm run start

# Debug mode (attach debugger)
npm run start:debug

# Build for production deployment
npm run build
npm run start:prod

Stopping the Application

To stop the development server:

# Press Ctrl + C in your terminal
# You'll see:
^C[Nest] 12345 - 12/01/2024, 12:40:00 PM LOG Application terminated

Using the NestJS CLI to Generate Code

The CLI is not just for creating projects - it's your assistant for generating all types of code!

Understanding CLI Schematics

"Schematics" are templates for generating code. Think of them as blueprints:

# General syntax:
nest generate <schematic> <name> [options]

# Or shorthand:
nest g <schematic> <name>

Available schematics:

SchematicGeneratesExample
moduleA new modulenest g module users
controllerA new controllernest g controller users
serviceA new service (provider)nest g service users
classA TypeScript classnest g class dto/create-user
interfaceA TypeScript interfacenest g interface user
guardAn authentication guardnest g guard auth
interceptorAn interceptornest g interceptor logging
pipeA validation pipenest g pipe validation
middlewareMiddlewarenest g middleware logger
filterException filternest g filter http-exception
gatewayWebSocket gatewaynest g gateway events
resolverGraphQL resolvernest g resolver users

Example: Creating a Complete Feature

Let's create a "users" feature with all necessary files:

# Option 1: Generate each piece individually
nest generate module users
nest generate controller users
nest generate service users

# Option 2: Use resource (generates everything at once!)
nest generate resource users

Let's try the resource generator:

$ nest generate resource users

? What transport layer do you use? (Use arrow keys)
❯ REST API
GraphQL (code first)
GraphQL (schema first)
Microservice (non-HTTP)
WebSockets

# Choose: REST API

? Would you like to generate CRUD entry points? (Y/n)
# Choose: Y (Yes)

CREATE src/users/users.controller.spec.ts (566 bytes)
CREATE src/users/users.controller.ts (894 bytes)
CREATE src/users/users.module.ts (247 bytes)
CREATE src/users/users.service.spec.ts (453 bytes)
CREATE src/users/users.service.ts (609 bytes)
CREATE src/users/dto/create-user.dto.ts (30 bytes)
CREATE src/users/dto/update-user.dto.ts (169 bytes)
CREATE src/users/entities/user.entity.ts (21 bytes)
UPDATE src/app.module.ts (312 bytes)

What just happened?

The CLI generated:

  1. ✅ Complete users module with controller and service
  2. ✅ CRUD endpoints (Create, Read, Update, Delete)
  3. ✅ DTOs (Data Transfer Objects) for validation
  4. ✅ Entity file for database modeling
  5. ✅ Unit tests for controller and service
  6. ✅ Automatically imported UsersModule in AppModule

All this in one command! Let's see what was created:

// src/users/users.module.ts
import { Module } from "@nestjs/common";
import { UsersService } from "./users.service";
import { UsersController } from "./users.controller";

@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}

// src/users/users.controller.ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from "@nestjs/common";
import { UsersService } from "./users.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";

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

@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}

@Get()
findAll() {
return this.usersService.findAll();
}

@Get(":id")
findOne(@Param("id") id: string) {
return this.usersService.findOne(+id);
}

@Patch(":id")
update(@Param("id") id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}

@Delete(":id")
remove(@Param("id") id: string) {
return this.usersService.remove(+id);
}
}

// src/users/users.service.ts
import { Injectable } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";

@Injectable()
export class UsersService {
create(createUserDto: CreateUserDto) {
return "This action adds a new user";
}

findAll() {
return `This action returns all users`;
}

findOne(id: number) {
return `This action returns a #${id} user`;
}

update(id: number, updateUserDto: UpdateUserDto) {
return `This action updates a #${id} user`;
}

remove(id: number) {
return `This action removes a #${id} user`;
}
}

Your new endpoints are immediately available:

GET    /users       # Get all users
GET /users/:id # Get one user
POST /users # Create user
PATCH /users/:id # Update user
DELETE /users/:id # Delete user

Test them in your browser:

CLI Options and Flags

Useful flags:

# Generate without spec (test) file
nest g service users --no-spec

# Generate in a specific directory
nest g controller admin/users

# Dry run (preview without creating files)
nest g module products --dry-run

# Generate with flat structure (no folder)
nest g service auth --flat

# Skip module import update
nest g controller users --no-flat --skip-import

Examples:

# Create admin feature in admin folder
nest g resource admin/users

# Result:
src/
admin/
users/
users.controller.ts
users.service.ts
users.module.ts

# Create a DTO class
nest g class dto/create-product --no-spec

# Result:
src/
dto/
create-product.ts

# Create an authentication guard
nest g guard auth/auth

# Result:
src/
auth/
auth.guard.ts
auth.guard.spec.ts

Understanding Generated File Patterns

Notice the CLI follows consistent patterns:

// Pattern 1: Feature folder structure
src/
users/
dto/ # Data Transfer Objects
create-user.dto.ts
update-user.dto.ts
entities/ # Database entities
user.entity.ts
users.controller.ts # HTTP routes
users.service.ts # Business logic
users.module.ts # Module definition
users.controller.spec.ts # Controller tests
users.service.spec.ts # Service tests

// Pattern 2: Naming conventions
users.controller.ts # <feature>.controller.ts
users.service.ts # <feature>.service.ts
users.module.ts # <feature>.module.ts
create-user.dto.ts # <action>-<feature>.dto.ts
user.entity.ts # <feature>.entity.ts

Why these patterns matter:

  • Team members know exactly where to find code
  • Files are automatically organized by feature
  • Imports and exports follow predictable paths
  • Easier to maintain as project grows

Common Development Workflows

Let's explore typical day-to-day workflows when developing with NestJS:

Workflow 1: Adding a New Feature

# Day 1: Create the feature structure
nest g resource products

# You get:
# - products.module.ts
# - products.controller.ts
# - products.service.ts
# - DTOs and entities
# - Test files

# Day 2: Implement business logic
# Edit products.service.ts with actual logic

# Day 3: Add validation
# Edit DTOs with validation decorators

# Day 4: Add database integration
# Implement entity and repository

# Day 5: Write tests
# Update .spec.ts files with real tests

Workflow 2: Modifying Existing Code

# Server is running with: npm run start:dev

# You edit a file:
# src/users/users.service.ts

# Hot reload happens automatically:
# [Nest] File change detected...
# [Nest] Application restarted successfully

# Test immediately in browser/Postman
# No manual server restart needed!

Workflow 3: Debugging Issues

# Option 1: Use console.log (simple)
@Injectable()
export class UsersService {
findAll() {
console.log('findAll called'); // Quick debug
return [];
}
}

# Option 2: Use debug mode with breakpoints
npm run start:debug

# Then attach VS Code debugger:
# 1. Set breakpoints in your code
# 2. Press F5 in VS Code
# 3. Debugger attaches automatically

Workflow 4: Running Tests

# Run all tests once
npm run test

# Watch mode (re-run on changes)
npm run test:watch

# Coverage report
npm run test:cov

# End-to-end tests
npm run test:e2e

# Test specific file
npm run test users.service.spec.ts

Example test output:

$ npm run test

PASS src/users/users.service.spec.ts
UsersService
✓ should be defined (15ms)
✓ should create a user (8ms)
✓ should find all users (5ms)

Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Time: 2.345s

Workflow 5: Preparing for Production

# Step 1: Run linting
npm run lint

# Step 2: Run tests
npm run test

# Step 3: Build the application
npm run build

# Check the output:
dist/
main.js
app.module.js
app.controller.js
app.service.js
# All TypeScript compiled to JavaScript

# Step 4: Test production build
npm run start:prod

# Step 5: Deploy dist/ folder to server

Project Organization Best Practices

As your project grows, organization becomes crucial. Let's explore best practices:

Feature-Based Structure

src/
common/ # Shared utilities
decorators/
guards/
interceptors/
filters/
pipes/
interfaces/
constants/

config/ # Configuration
database.config.ts
app.config.ts

users/ # User feature
dto/
entities/
users.controller.ts
users.service.ts
users.module.ts

products/ # Product feature
dto/
entities/
products.controller.ts
products.service.ts
products.module.ts

auth/ # Authentication feature
strategies/
guards/
auth.controller.ts
auth.service.ts
auth.module.ts

main.ts # Entry point
app.module.ts # Root module

Shared Module Pattern

// src/common/common.module.ts
import { Module, Global } from "@nestjs/common";
import { LoggerService } from "./logger.service";
import { ConfigService } from "./config.service";

@Global() // Available everywhere without importing
@Module({
providers: [LoggerService, ConfigService],
exports: [LoggerService, ConfigService],
})
export class CommonModule {}

// Now any module can use these services without importing CommonModule
@Injectable()
export class UsersService {
constructor(
private logger: LoggerService, // Available globally
private config: ConfigService // Available globally
) {}
}

Core Module Pattern

// src/core/core.module.ts
import { Module } from "@nestjs/common";
import { DatabaseModule } from "./database/database.module";
import { CacheModule } from "./cache/cache.module";
import { QueueModule } from "./queue/queue.module";

@Module({
imports: [DatabaseModule, CacheModule, QueueModule],
exports: [DatabaseModule, CacheModule, QueueModule],
})
export class CoreModule {}

// Import once in AppModule
@Module({
imports: [CoreModule, UsersModule, ProductsModule],
})
export class AppModule {}

Environment Configuration Pattern

// src/config/configuration.ts
export default () => ({
port: parseInt(process.env.PORT, 10) || 3000,
database: {
host: process.env.DATABASE_HOST || "localhost",
port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
username: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || "1h",
},
});

// Usage in main.ts
import { ConfigService } from "@nestjs/config";

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);

const port = configService.get & lt;
number > "port";
await app.listen(port);
}

Common Misconceptions

❌ Misconception: "I need to restart the server after every change"

Reality: Development mode with hot reload automatically restarts when files change.

Why this matters: Manual restarts waste time and break your flow.

Example:

# ❌ Wrong workflow:
# 1. Edit file
# 2. Ctrl+C to stop server
# 3. npm run start:dev
# 4. Wait for startup
# 5. Test change

# ✅ Right workflow:
# 1. npm run start:dev once at beginning
# 2. Edit files
# 3. Save
# 4. Changes apply automatically
# 5. Test immediately

❌ Misconception: "Generated code is production-ready"

Reality: Generated code is a starting point - you must implement actual logic.

Why this matters: CLI generates boilerplate, not business logic.

Example:

// Generated code (not production-ready):
@Injectable()
export class UsersService {
create(createUserDto: CreateUserDto) {
return "This action adds a new user"; // Just a placeholder!
}
}

// Production code (what you need to write):
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>
) {}

async create(createUserDto: CreateUserDto): Promise<User> {
// Actual implementation
const existingUser = await this.usersRepository.findOne({
where: { email: createUserDto.email },
});

if (existingUser) {
throw new ConflictException("Email already exists");
}

const user = this.usersRepository.create(createUserDto);
return await this.usersRepository.save(user);
}
}

❌ Misconception: "I should modify files in node_modules/"

Reality: NEVER modify node_modules - changes are lost when you reinstall.

Why this matters: node_modules is generated from package.json and should be treated as read-only.

Example:

# ❌ Wrong: Editing installed packages
node_modules/@nestjs/common/decorators/core/module.decorator.d.ts
# Changes lost on: npm install

# ✅ Right: Create your own files
src/common/decorators/custom-module.decorator.ts
# Your code, under version control

❌ Misconception: "main.ts shouldn't be changed"

Reality: main.ts is where you configure your application - you'll enhance it frequently.

Why this matters: Global configuration like CORS, validation, and middleware goes in main.ts.

Example:

// Basic main.ts (initial):
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}

// Enhanced main.ts (real projects):
async function bootstrap() {
const app = await NestFactory.create(AppModule);

// Enable CORS
app.enableCors({
origin: process.env.FRONTEND_URL,
credentials: true,
});

// Global validation
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
})
);

// Global exception filter
app.useGlobalFilters(new HttpExceptionFilter());

// API prefix
app.setGlobalPrefix("api/v1");

// Swagger documentation
const config = new DocumentBuilder()
.setTitle("My API")
.setVersion("1.0")
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("api/docs", app, document);

await app.listen(3000);
}

❌ Misconception: "All my code should go in the src/ root"

Reality: Organize code into feature folders for maintainability.

Why this matters: Flat structures become unmanageable as projects grow.

Example:

# ❌ Bad: Everything in src/ root
src/
users.controller.ts
users.service.ts
products.controller.ts
products.service.ts
orders.controller.ts
orders.service.ts
auth.controller.ts
auth.service.ts
create-user.dto.ts
create-product.dto.ts
# 50+ files in one folder - chaos!

# ✅ Good: Feature-based organization
src/
users/
users.controller.ts
users.service.ts
users.module.ts
dto/
products/
products.controller.ts
products.service.ts
products.module.ts
dto/
# Clear structure, easy to navigate

Troubleshooting Common Setup Issues

Problem: "nest: command not found"

Symptoms: After installing CLI, nest command doesn't work

Common Causes:

  1. CLI not installed globally (80%)
  2. Terminal not restarted (15%)
  3. npm global path not in system PATH (5%)

Solution:

# Step 1: Verify installation
npm list -g @nestjs/cli

# If not installed:
npm install -g @nestjs/cli

# Step 2: Close and reopen terminal

# Step 3: Try again
nest --version

# If still not working, use npx:
npx @nestjs/cli new my-project

Problem: "Port 3000 is already in use"

Symptoms: Error: listen EADDRINUSE: address already in use :::3000

Common Causes:

  1. Another NestJS instance running (60%)
  2. Other application using port 3000 (30%)
  3. Zombie process from crash (10%)

Solution:

# Option 1: Find and kill the process
# macOS/Linux:
lsof -i :3000
kill -9 <PID>

# Windows:
netstat -ano | findstr :3000
taskkill /PID <PID> /F

# Option 2: Use a different port
# Edit src/main.ts:
await app.listen(3001); // Use port 3001 instead

# Option 3: Use environment variable
await app.listen(process.env.PORT || 3000);
# Then run: PORT=3001 npm run start:dev

Problem: "Cannot find module '@nestjs/common'"

Symptoms: Import errors, application won't start

Common Causes:

  1. Dependencies not installed (90%)
  2. node_modules corrupted (10%)

Solution:

# Step 1: Install dependencies
npm install

# Step 2: If still failing, clean install
rm -rf node_modules package-lock.json
npm install

# Step 3: Verify installation
npm list @nestjs/common
# Should show version number

Problem: "Hot reload not working"

Symptoms: Changes don't reflect, must restart manually

Common Causes:

  1. Running in production mode (70%)
  2. File not saved (20%)
  3. TypeScript compilation error (10%)

Solution:

# Verify you're using dev mode
ps aux | grep nest

# Should see: nest start --watch
# If not, restart with:
npm run start:dev

# Check for TypeScript errors in output:
[Nest] Error: Cannot find module...
# Fix the error and save again

Problem: "Module not found" after generating

Symptoms: Generated module can't be imported

Common Causes:

  1. Module not imported in parent module (95%)
  2. Wrong import path (5%)

Solution:

// Check app.module.ts
@Module({
imports: [
UsersModule, // Make sure your module is imported
],
})
export class AppModule {}

// Verify import path:
import { UsersModule } from "./users/users.module"; // ✅ Correct
import { UsersModule } from "./users/users"; // ❌ Wrong

Performance Implications

Development Mode Performance

Development mode trade-offs:

# Development mode (npm run start:dev)
Startup time: 2-5 seconds
Hot reload: 1-2 seconds per change
Memory usage: 100-150 MB

# Production mode (npm run start:prod)
Startup time: 0.5-1 second
No hot reload: N/A
Memory usage: 50-80 MB

Why development is slower:

  • TypeScript compilation on every change
  • Source maps generated for debugging
  • Additional logging and error details
  • File watching overhead

This is normal and expected! Development mode optimizes for developer experience, not performance.

Build Performance

Optimizing build times:

// tsconfig.json - Faster builds
{
"compilerOptions": {
"incremental": true, // Only recompile changed files
"skipLibCheck": true, // Skip type checking .d.ts files
"noEmit": false, // Generate JavaScript
"removeComments": true // Smaller output files
}
}

// Typical build times:
// Small project (< 50 files): 5-10 seconds
// Medium project (100-200 files): 15-30 seconds
// Large project (500+ files): 1-2 minutes

Reducing Startup Time

// Lazy load modules (covered in future articles)
@Module({
imports: [
// Load immediately:
CoreModule,
AuthModule,

// Lazy load (only when needed):
{
path: "admin",
loadChildren: () => import("./admin/admin.module"),
},
],
})
export class AppModule {}

Check Your Understanding

Quick Quiz

  1. What command creates a new NestJS project? <details> <summary>Show Answer</summary>

    nest new project-name

    This command:

    • Creates project folder
    • Generates initial files
    • Installs dependencies
    • Sets up TypeScript configuration </details>
  2. Which file is the entry point of your application? <details> <summary>Show Answer</summary>

    src/main.ts is the entry point.

    It contains the bootstrap() function that:

    • Creates the NestJS application
    • Configures global settings
    • Starts the HTTP server

    This is always the first file executed when your app starts. </details>

  3. What's the difference between npm run start and npm run start:dev? <details> <summary>Show Answer</summary>

    npm run start (production mode):

    • Runs once without watching files
    • Faster startup
    • No hot reload
    • Use for production deployments

    npm run start:dev (development mode):

    • Watches files for changes
    • Automatically restarts on changes
    • Includes source maps for debugging
    • Use during development

    Always use start:dev when developing! </details>

  4. What does nest generate resource users create? <details> <summary>Show Answer</summary>

    Creates a complete feature with:

    • ✅ users.module.ts - Module definition
    • ✅ users.controller.ts - HTTP routes
    • ✅ users.service.ts - Business logic
    • ✅ DTOs (create-user.dto.ts, update-user.dto.ts)
    • ✅ Entity (user.entity.ts)
    • ✅ Test files (.spec.ts)
    • ✅ CRUD endpoints (if you chose Yes)
    • ✅ Automatic import in AppModule

    All from one command! </details>

Hands-On Exercise

Challenge: Create a "products" feature with full CRUD operations.

Requirements:

  1. Generate the products resource
  2. Add a route that returns all products
  3. Test it in your browser
  4. Modify the service to return custom data

<details> <summary>Show Solution</summary>

Step 1: Generate the resource

nest generate resource products
# Choose: REST API
# Choose: Y (generate CRUD)

Step 2: The controller already has routes!

// src/products/products.controller.ts
// GET /products route already exists
@Get()
findAll() {
return this.productsService.findAll();
}

Step 3: Test in browser

Visit: http://localhost:3000/products
You'll see: "This action returns all products"

Step 4: Modify service with custom data

// src/products/products.service.ts
@Injectable()
export class ProductsService {
private products = [
{ id: 1, name: "Laptop", price: 999 },
{ id: 2, name: "Mouse", price: 25 },
{ id: 3, name: "Keyboard", price: 75 },
];

findAll() {
return this.products;
}

findOne(id: number) {
return this.products.find((p) => p.id === id);
}
}

Step 5: Test again

Visit: http://localhost:3000/products
Now you see: [{"id":1,"name":"Laptop","price":999},...]

Explanation:

  • Resource generator created complete structure
  • Controller handles HTTP routing
  • Service contains the data and logic
  • Changes reflect immediately with hot reload </details>

Summary: Key Takeaways

🎯 Project Setup Essentials:

  • Install NestJS CLI globally: npm i -g @nestjs/cli
  • Create projects: nest new project-name
  • Always use npm run start:dev during development
  • Hot reload saves time - no manual restarts needed

📁 Project Structure:

  • src/main.ts - Application entry point
  • src/app.module.ts - Root module that imports everything
  • src/app.controller.ts - Sample HTTP routes
  • src/app.service.ts - Sample business logic
  • Feature folders (users/, products/) organize code by domain

🛠️ CLI Power Tools:

  • nest generate resource - Creates complete features
  • nest generate controller - Creates controllers
  • nest generate service - Creates services
  • nest generate module - Creates modules
  • Add --no-spec to skip test files
  • Use --dry-run to preview changes

⚡ Development Workflow:

  • Start with: npm run start:dev
  • Edit files and save
  • Hot reload applies changes automatically
  • Test immediately in browser
  • Build for production: npm run build

🏗️ Best Practices:

  • Organize by feature, not by type
  • Use CLI generators to maintain consistency
  • Keep controllers thin - logic goes in services
  • Import feature modules in AppModule
  • Never modify node_modules/

⚠️ Remember:

  • Generated code is boilerplate - implement real logic
  • Development mode is slower (but worth it for hot reload)
  • Port 3000 is default - change if needed
  • CLI requires Node 18+ and npm 9+

Practical Exercises:

  • Create a simple todo API with CRUD operations
  • Build a blog API with posts and comments
  • Practice generating different types of components

Pro Tips:

  • Keep your dev server running all day
  • Use the CLI for everything - don't create files manually
  • Explore the generated code - it teaches best practices
  • Check the logs - they show exactly what's happening

Ready to build real routes and handle HTTP requests? Let's continue to the Controllers article!

Version Information

Tested with:

  • Node.js: v18.x, v20.x, v22.x
  • NestJS CLI: v10.x
  • npm: v9.x, v10.x

System Requirements:

  • Node.js 18 or higher (required)
  • npm 9 or higher (recommended)
  • 4 GB RAM minimum
  • 1 GB free disk space

Known Issues:

  • ⚠️ Windows: Use PowerShell or Git Bash (not CMD)
  • ⚠️ macOS: May need sudo for global npm installs
  • ⚠️ Linux: Configure npm global path without sudo

CLI Versions:

  • NestJS CLI 10.x - Current stable
  • Breaking changes from v9: None significant
  • Backwards compatible with NestJS 9.x projects