This document explains step-by-step how NestJS handles the request curl http://localhost:3000/todos.

📋 Request Overview

curl http://localhost:3000/todos

HTTP Request Details: - Method: GET - URL: http://localhost:3000/todos - Path: /todos - Headers: Default curl headers (no custom headers)


🔄 Complete Request Flow

Step 1: Application Bootstrap (main.ts)

File: src/main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // ... CORS configuration ...
  await app.listen(3000);
}

What happens: 1. NestFactory.create(AppModule) creates the NestJS application instance 2. The application reads AppModule to understand the module structure 3. NestJS dependency injection container is initialized 4. All modules, controllers, and services are registered 5. HTTP server starts listening on port 3000

Result: Application is ready to receive HTTP requests.


Step 2: Module Registration (app.module.ts)

File: src/app.module.ts

@Module({
  imports: [TodosModule],  // ← TodosModule is imported here
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

What happens: 1. NestJS scans AppModule decorator 2. Finds TodosModule in the imports array 3. Loads TodosModule and registers its controllers and providers 4. Makes TodosController and TodosService available to the application

Result: TodosController is registered and can handle requests.


Step 3: TodosModule Registration (todos.module.ts)

File: src/todos/todos.module.ts

@Module({
  controllers: [TodosController],  // ← Controller registered
  providers: [TodosService],       // ← Service registered
})
export class TodosModule {}

What happens: 1. NestJS registers TodosController as a controller 2. NestJS registers TodosService as a provider 3. Creates dependency injection relationship: - TodosController depends on TodosService - NestJS will inject TodosService into TodosController constructor

Result: - Route /todos is mapped to TodosController - TodosService instance is created and ready for injection


Step 4: HTTP Request Arrives

When the request arrives:

GET /todos HTTP/1.1
Host: localhost:3000

What happens: 1. Express.js (underlying HTTP server) receives the request 2. NestJS routing system intercepts the request 3. Router matches the path /todos to TodosController 4. Router checks for a handler method matching GET method


Step 5: Route Matching (todos.controller.ts)

File: src/todos/todos.controller.ts

@Controller('todos')  // ← Base route: /todos
export class TodosController {
  @Get()              // ← Handles GET requests to /todos
  findAll() {
    return {
      success: true,
      data: this.todosService.findAll(),
    };
  }
}

What happens: 1. @Controller('todos') decorator sets base route to /todos 2. @Get() decorator marks findAll() method to handle GET requests 3. Route matcher finds: GET /todosfindAll() method 4. NestJS prepares to execute findAll()

Route Matching Logic: - Request path: /todos - Controller base: todos - Method decorator: @Get() (no additional path) - Match!findAll() method will be called


Step 6: Dependency Injection

Before executing findAll(), NestJS injects dependencies:

constructor(private readonly todosService: TodosService) {}

What happens: 1. NestJS checks TodosController constructor 2. Finds TodosService as a dependency 3. Looks up TodosService in the dependency injection container 4. Finds the TodosService instance (created when module was loaded) 5. Injects the service instance into the controller 6. Controller now has access to this.todosService

Dependency Injection Flow:

TodosController needs TodosService
    ↓
Check TodosModule providers
    ↓
Find TodosService
    ↓
Create instance (if not exists)
    ↓
Inject into TodosController

Step 7: Method Execution

Now findAll() method executes:

findAll() {
  return {
    success: true,
    data: this.todosService.findAll(),  // ← Calls service method
  };
}

What happens: 1. findAll() method is called 2. Method body executes: this.todosService.findAll() 3. This calls the service method (see Step 8) 4. Service returns data 5. Controller wraps the data in a response object 6. Returns the response object


Step 8: Service Method Execution (todos.service.ts)

File: src/todos/todos.service.ts

@Injectable()
export class TodosService {
  private todos: Todo[] = [
    {
      id: '1',
      title: 'Learn NestJS',
      description: 'Complete the NestJS tutorial',
      completed: false,
      createdAt: new Date(),
      updatedAt: new Date(),
    },
    {
      id: '2',
      title: 'Build RESTful API',
      description: 'Create a complete REST API with all methods',
      completed: true,
      createdAt: new Date(),
      updatedAt: new Date(),
    },
  ];

  findAll(): Todo[] {
    return this.todos;  // ← Returns the array
  }
}

What happens: 1. findAll() method is called on TodosService 2. Method accesses this.todos (the in-memory array) 3. Returns the entire array of todos 4. Returns to the controller

Data returned:

[
  {
    id: '1',
    title: 'Learn NestJS',
    description: 'Complete the NestJS tutorial',
    completed: false,
    createdAt: Date,
    updatedAt: Date,
  },
  {
    id: '2',
    title: 'Build RESTful API',
    description: 'Create a complete REST API with all methods',
    completed: true,
    createdAt: Date,
    updatedAt: Date,
  }
]

Step 9: Response Formation

Back in the controller:

return {
  success: true,
  data: this.todosService.findAll(),  // ← Service returned the array
};

What happens: 1. Service returned the todos array 2. Controller wraps it in a response object: typescript { success: true, data: [/* todos array */] } 3. Controller returns this object


Step 10: Response Serialization

NestJS automatically: 1. Takes the returned object 2. Serializes it to JSON 3. Sets appropriate HTTP headers: - Content-Type: application/json - Content-Length: <size> 4. Sets HTTP status code: 200 OK (default for successful GET)


Step 11: HTTP Response Sent

Final HTTP Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 456

{
  "success": true,
  "data": [
    {
      "id": "1",
      "title": "Learn NestJS",
      "description": "Complete the NestJS tutorial",
      "completed": false,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "2",
      "title": "Build RESTful API",
      "description": "Create a complete REST API with all methods",
      "completed": true,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

📊 Complete Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│ 1. HTTP Request Arrives                                      │
│    GET http://localhost:3000/todos                          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Express.js Receives Request                              │
│    - Parses HTTP request                                     │
│    - Extracts method, path, headers                          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. NestJS Router Matches Route                              │
│    - Checks route table                                      │
│    - Finds: GET /todos → TodosController.findAll()           │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Dependency Injection                                     │
│    - Injects TodosService into TodosController              │
│    - Creates service instance if needed                      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Controller Method Execution                              │
│    TodosController.findAll()                                 │
│    ↓                                                          │
│    Calls: this.todosService.findAll()                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Service Method Execution                                 │
│    TodosService.findAll()                                    │
│    ↓                                                          │
│    Returns: this.todos (array of todos)                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 7. Response Formation                                       │
│    Controller wraps data:                                    │
│    { success: true, data: [...] }                           │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 8. JSON Serialization                                       │
│    - Converts object to JSON string                          │
│    - Sets Content-Type: application/json                     │
│    - Sets status: 200 OK                                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 9. HTTP Response Sent to Client                             │
│    - Sends JSON response                                     │
│    - Closes connection                                        │
└─────────────────────────────────────────────────────────────┘

🔍 Key Concepts Explained

1. Decorators

@Controller('todos') - Registers the class as a controller - Sets the base route path to /todos - Makes the class available to NestJS routing system

@Get() - Marks a method to handle GET requests - Can take an optional path parameter: @Get('custom-path') - Without parameter, handles requests to the base route

2. Dependency Injection

How it works:

constructor(private readonly todosService: TodosService) {}
  • NestJS automatically detects TodosService as a dependency
  • Looks it up in the module's providers
  • Creates or reuses an instance
  • Injects it into the constructor
  • Makes it available as this.todosService

Benefits: - Loose coupling (controller doesn't create service) - Testability (easy to mock in tests) - Single responsibility (service handles business logic)

3. Request-Response Cycle

  1. Request → HTTP request arrives
  2. Routing → NestJS matches route to controller method
  3. Execution → Controller method executes
  4. Service → Business logic in service
  5. Response → Data returned and serialized
  6. Send → HTTP response sent to client

4. In-Memory Storage

Current Implementation:

private todos: Todo[] = [/* array */];
  • Data stored in memory (lost on server restart)
  • Good for learning and development
  • In production, use a database (PostgreSQL, MongoDB, etc.)

🧪 Testing the Flow

1. Start the Server

npm run start:dev

2. Make the Request

curl http://localhost:3000/todos

3. Expected Response

{
  "success": true,
  "data": [
    {
      "id": "1",
      "title": "Learn NestJS",
      "description": "Complete the NestJS tutorial",
      "completed": false,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "2",
      "title": "Build RESTful API",
      "description": "Create a complete REST API with all methods",
      "completed": true,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z"
    }
  ]
}

📝 Summary

Request Flow Summary:

  1. ✅ HTTP request arrives at port 3000
  2. ✅ Express.js receives and parses the request
  3. ✅ NestJS router matches /todos to TodosController
  4. @Get() decorator matches GET method to findAll()
  5. ✅ NestJS injects TodosService into controller
  6. ✅ Controller calls this.todosService.findAll()
  7. ✅ Service returns the todos array from memory
  8. ✅ Controller wraps data in response object
  9. ✅ NestJS serializes to JSON
  10. ✅ HTTP 200 response sent with JSON body

Total Execution Time: Typically < 10ms for in-memory operations


🎓 Learning Points

  1. Decorators control routing and behavior
  2. Dependency Injection provides services automatically
  3. Controllers handle HTTP requests/responses
  4. Services contain business logic
  5. Modules organize and register components
  6. NestJS orchestrates the entire flow automatically

This demonstrates the power of NestJS: you write simple, clean code, and NestJS handles all the complex routing, dependency injection, and request/response management for you!


Detailed Request Flow: POST /todos

This document explains step-by-step how NestJS handles the POST request to create a new todo.

📋 Request Overview

curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"New Todo","description":"Learn NestJS","completed":false}'

HTTP Request Details: - Method: POST - URL: http://localhost:3000/todos - Path: /todos - Headers: - Content-Type: application/json (tells server the body is JSON) - Body: JSON data with todo information

Request Body:

{
  "title": "New Todo",
  "description": "Learn NestJS",
  "completed": false
}

🔄 Complete Request Flow

Step 1: Application Bootstrap (Same as GET)

File: src/main.ts

The application is already running from the previous GET request. The same bootstrap process applies: - NestJS application instance is created - All modules are registered - Server is listening on port 3000

Result: Application is ready to receive HTTP requests.


Step 2: HTTP Request Arrives with Body

When the POST request arrives:

POST /todos HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Content-Length: 65

{"title":"New Todo","description":"Learn NestJS","completed":false}

What happens: 1. Express.js (underlying HTTP server) receives the request 2. Express.js detects Content-Type: application/json header 3. Express.js reads the request body (the JSON string) 4. Express.js parses the JSON string into a JavaScript object 5. NestJS routing system intercepts the request 6. Router matches the path /todos to TodosController 7. Router checks for a handler method matching POST method

Key Difference from GET: - POST requests have a request body that needs to be parsed - The body contains the data to create a new todo - Express.js automatically parses JSON when Content-Type: application/json is set


Step 3: Route Matching (todos.controller.ts)

File: src/todos/todos.controller.ts

@Controller('todos')  // ← Base route: /todos
export class TodosController {
  @Post()              // ← Handles POST requests to /todos
  @HttpCode(HttpStatus.CREATED)  // ← Sets status code to 201
  create(@Body() createTodoDto: CreateTodoDto) {  // ← Extracts body
    return {
      success: true,
      message: 'Todo created successfully',
      data: this.todosService.create(createTodoDto),
    };
  }
}

What happens: 1. @Controller('todos') decorator sets base route to /todos 2. @Post() decorator marks create() method to handle POST requests 3. @HttpCode(HttpStatus.CREATED) sets the response status code to 201 (Created) 4. Route matcher finds: POST /todoscreate() method 5. NestJS prepares to execute create()

Route Matching Logic: - Request path: /todos - Request method: POST - Controller base: todos - Method decorator: @Post() (no additional path) - Match!create() method will be called

Important Decorators: - @Post() - Handles POST HTTP method - @HttpCode(HttpStatus.CREATED) - Sets response status to 201 (instead of default 200) - @Body() - Extracts and validates the request body


Step 4: Request Body Extraction

The @Body() decorator extracts the request body:

create(@Body() createTodoDto: CreateTodoDto) {
  // createTodoDto now contains the parsed JSON data
}

What happens: 1. NestJS reads the parsed JSON object from Express.js 2. @Body() decorator extracts the request body 3. NestJS attempts to map the JSON object to CreateTodoDto class 4. The body data is assigned to createTodoDto parameter

Body Data Mapping:

// Request body (JSON):
{
  "title": "New Todo",
  "description": "Learn NestJS",
  "completed": false
}

// Mapped to CreateTodoDto:
createTodoDto = {
  title: "New Todo",
  description: "Learn NestJS",
  completed: false
}

DTO (Data Transfer Object) Purpose: - CreateTodoDto defines the expected structure of the request body - Provides type safety in TypeScript - Can be used for validation (in intermediate/advanced levels)


Step 5: DTO Structure (create-todo.dto.ts)

File: src/todos/dto/create-todo.dto.ts

export class CreateTodoDto {
  title: string;           // Required field
  description?: string;     // Optional field (the ? means optional)
  completed?: boolean;      // Optional field, defaults to false if not provided
}

What happens: 1. NestJS maps the JSON body to this DTO structure 2. title is required (must be present in request) 3. description is optional (can be omitted) 4. completed is optional (will default to false if not provided)

DTO Benefits: - Type Safety: TypeScript knows the structure - Documentation: Clear contract of what data is expected - Validation: Can add validation decorators (in intermediate level) - Maintainability: Single source of truth for request structure


Step 6: Dependency Injection (Same as GET)

Before executing create(), NestJS injects dependencies:

constructor(private readonly todosService: TodosService) {}

What happens: 1. NestJS checks TodosController constructor 2. Finds TodosService as a dependency 3. Looks up TodosService in the dependency injection container 4. Finds the existing TodosService instance (reused from previous requests) 5. Controller has access to this.todosService

Note: The same service instance is reused (singleton pattern by default in NestJS).


Step 7: Controller Method Execution

Now create() method executes:

create(@Body() createTodoDto: CreateTodoDto) {
  return {
    success: true,
    message: 'Todo created successfully',
    data: this.todosService.create(createTodoDto),  // ← Calls service
  };
}

What happens: 1. create() method is called with the DTO data 2. Method body executes: this.todosService.create(createTodoDto) 3. Passes the DTO to the service method (see Step 8) 4. Service creates the todo and returns it 5. Controller wraps the created todo in a response object 6. Returns the response object

Parameter Flow:

Request Body (JSON)
    ↓
@Body() decorator extracts
    ↓
Mapped to CreateTodoDto
    ↓
Passed to createTodoDto parameter
    ↓
Passed to service.create()

Step 8: Service Method Execution (todos.service.ts)

File: src/todos/todos.service.ts

@Injectable()
export class TodosService {
  private todos: Todo[] = [/* existing todos */];

  create(createTodoDto: CreateTodoDto): Todo {
    const todo: Todo = {
      id: Date.now().toString(),              // ← Generate unique ID
      title: createTodoDto.title,            // ← From request body
      description: createTodoDto.description, // ← From request body (optional)
      completed: createTodoDto.completed || false, // ← Default to false if not provided
      createdAt: new Date(),                 // ← Set creation timestamp
      updatedAt: new Date(),                 // ← Set update timestamp
    };
    this.todos.push(todo);                   // ← Add to in-memory array
    return todo;                             // ← Return created todo
  }
}

What happens step-by-step:

  1. ID Generation: typescript id: Date.now().toString() - Uses current timestamp as unique ID - Example: "1705312200000" (milliseconds since epoch) - In production, use UUID or database auto-increment

  2. Title Assignment: typescript title: createTodoDto.title - Takes title from request body: "New Todo"

  3. Description Assignment: typescript description: createTodoDto.description - Takes description from request body: "Learn NestJS" - If not provided, will be undefined

  4. Completed Status: typescript completed: createTodoDto.completed || false - Uses completed from request body if provided - Defaults to false if not provided (using || operator)

  5. Timestamps: typescript createdAt: new Date(), updatedAt: new Date() - Sets both timestamps to current date/time - createdAt never changes - updatedAt changes when todo is modified

  6. Storage: typescript this.todos.push(todo) - Adds the new todo to the in-memory array - Array now contains the new todo

  7. Return: typescript return todo - Returns the created todo object - Returns to controller

Created Todo Object:

{
  id: "1705312200000",
  title: "New Todo",
  description: "Learn NestJS",
  completed: false,
  createdAt: Date,  // Current timestamp
  updatedAt: Date   // Current timestamp
}

Before vs After:

// Before: 2 todos in array
this.todos = [
  { id: '1', title: 'Learn NestJS', ... },
  { id: '2', title: 'Build RESTful API', ... }
]

// After: 3 todos in array
this.todos = [
  { id: '1', title: 'Learn NestJS', ... },
  { id: '2', title: 'Build RESTful API', ... },
  { id: '1705312200000', title: 'New Todo', ... }  // ← New todo added
]

Step 9: Response Formation

Back in the controller:

return {
  success: true,
  message: 'Todo created successfully',
  data: this.todosService.create(createTodoDto),  // ← Service returned the created todo
};

What happens: 1. Service returned the newly created todo object 2. Controller wraps it in a response object: typescript { success: true, message: 'Todo created successfully', data: { id: "1705312200000", title: "New Todo", description: "Learn NestJS", completed: false, createdAt: Date, updatedAt: Date } } 3. Controller returns this object

Response Structure: - success: true - Indicates operation was successful - message - Human-readable success message - data - The created todo object


Step 10: HTTP Status Code

The @HttpCode(HttpStatus.CREATED) decorator sets the status:

@Post()
@HttpCode(HttpStatus.CREATED)  // ← Sets status to 201
create(@Body() createTodoDto: CreateTodoDto) {
  // ...
}

What happens: 1. HttpStatus.CREATED equals 201 2. NestJS sets HTTP status code to 201 Created 3. This is the standard status code for successful resource creation

HTTP Status Codes: - 200 OK - Successful GET, PUT, PATCH - 201 Created - Successful POST (resource created) - 204 No Content - Successful DELETE - 400 Bad Request - Invalid request data - 404 Not Found - Resource not found - 500 Internal Server Error - Server error


Step 11: Response Serialization

NestJS automatically: 1. Takes the returned object 2. Serializes it to JSON 3. Sets appropriate HTTP headers: - Content-Type: application/json - Content-Length: <size> 4. Sets HTTP status code: 201 Created (from @HttpCode decorator)

Serialization Process:

// Controller returns:
{
  success: true,
  message: 'Todo created successfully',
  data: {
    id: "1705312200000",
    title: "New Todo",
    description: "Learn NestJS",
    completed: false,
    createdAt: Date,  // JavaScript Date object
    updatedAt: Date   // JavaScript Date object
  }
}

// NestJS serializes to JSON:
{
  "success": true,
  "message": "Todo created successfully",
  "data": {
    "id": "1705312200000",
    "title": "New Todo",
    "description": "Learn NestJS",
    "completed": false,
    "createdAt": "2024-01-15T12:30:00.000Z",  // Date converted to ISO string
    "updatedAt": "2024-01-15T12:30:00.000Z"   // Date converted to ISO string
  }
}

Note: JavaScript Date objects are automatically converted to ISO 8601 strings in JSON.


Step 12: HTTP Response Sent

Final HTTP Response:

HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 234

{
  "success": true,
  "message": "Todo created successfully",
  "data": {
    "id": "1705312200000",
    "title": "New Todo",
    "description": "Learn NestJS",
    "completed": false,
    "createdAt": "2024-01-15T12:30:00.000Z",
    "updatedAt": "2024-01-15T12:30:00.000Z"
  }
}

Response Headers: - HTTP/1.1 201 Created - Status line (201 instead of 200) - Content-Type: application/json - Response is JSON - Content-Length: 234 - Size of response body in bytes


📊 Complete Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│ 1. HTTP POST Request Arrives                                │
│    POST http://localhost:3000/todos                         │
│    Body: {"title":"New Todo","description":"Learn NestJS"} │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Express.js Receives Request                             │
│    - Parses HTTP request                                     │
│    - Detects Content-Type: application/json                 │
│    - Reads and parses JSON body                             │
│    - Extracts method, path, headers, body                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. NestJS Router Matches Route                             │
│    - Checks route table                                      │
│    - Finds: POST /todos → TodosController.create()           │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Request Body Extraction                                  │
│    @Body() decorator extracts JSON body                     │
│    Maps to CreateTodoDto structure                          │
│    createTodoDto = { title, description, completed }       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Dependency Injection                                     │
│    - Injects TodosService into TodosController              │
│    - Reuses existing service instance                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Controller Method Execution                             │
│    TodosController.create(createTodoDto)                     │
│    ↓                                                          │
│    Calls: this.todosService.create(createTodoDto)          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 7. Service Method Execution                                 │
│    TodosService.create(createTodoDto)                        │
│    ↓                                                          │
│    1. Generate ID (Date.now())                              │
│    2. Create Todo object                                    │
│    3. Add to todos array (this.todos.push())                │
│    4. Return created todo                                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 8. Response Formation                                       │
│    Controller wraps created todo:                           │
│    {                                                         │
│      success: true,                                          │
│      message: 'Todo created successfully',                  │
│      data: { /* created todo */ }                           │
│    }                                                         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 9. Status Code Setting                                      │
│    @HttpCode(HttpStatus.CREATED) sets status to 201         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 10. JSON Serialization                                      │
│     - Converts object to JSON string                         │
│     - Converts Date objects to ISO strings                   │
│     - Sets Content-Type: application/json                     │
│     - Sets status: 201 Created                               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│ 11. HTTP Response Sent to Client                            │
│     - Sends JSON response with 201 status                   │
│     - Closes connection                                      │
└─────────────────────────────────────────────────────────────┘

🔍 Key Concepts Explained

1. POST vs GET

GET Request: - No request body - Used to retrieve data - Idempotent (same request = same result) - Returns 200 OK

POST Request: - Has request body with data - Used to create new resources - Not idempotent (same request = different result each time) - Returns 201 Created

2. Request Body Parsing

How it works:

@Body() createTodoDto: CreateTodoDto
  • @Body() decorator extracts the request body
  • Express.js has already parsed JSON to JavaScript object
  • NestJS maps the object to the DTO class
  • Provides type safety and structure validation

Body Extraction Process:

HTTP Request Body (JSON string)
    ↓
Express.js JSON parser
    ↓
JavaScript object
    ↓
@Body() decorator
    ↓
CreateTodoDto instance
    ↓
Method parameter

3. DTO (Data Transfer Object)

Purpose: - Defines the structure of incoming data - Provides TypeScript type safety - Documents the API contract - Can be extended with validation (intermediate level)

Example:

export class CreateTodoDto {
  title: string;        // Required
  description?: string; // Optional
  completed?: boolean;  // Optional
}

4. HTTP Status Codes

201 Created: - Standard response for successful POST requests - Indicates a new resource was created - Should include the created resource in response body - Can include Location header with resource URL

Set in NestJS:

@HttpCode(HttpStatus.CREATED)  // Sets status to 201

5. ID Generation

Current Implementation:

id: Date.now().toString()
  • Uses timestamp as ID
  • Simple but not ideal for production
  • Better alternatives:
  • UUID: uuidv4() - Universally unique
  • Database auto-increment - Sequential IDs
  • Snowflake IDs - Distributed systems

6. In-Memory Storage

Current Implementation:

this.todos.push(todo)
  • Data stored in memory
  • Lost when server restarts
  • Good for learning
  • In production, use database (PostgreSQL, MongoDB, etc.)

🧪 Testing the Flow

1. Start the Server

npm run start:dev

2. Make the POST Request

curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"New Todo","description":"Learn NestJS","completed":false}'

3. Expected Response

{
  "success": true,
  "message": "Todo created successfully",
  "data": {
    "id": "1705312200000",
    "title": "New Todo",
    "description": "Learn NestJS",
    "completed": false,
    "createdAt": "2024-01-15T12:30:00.000Z",
    "updatedAt": "2024-01-15T12:30:00.000Z"
  }
}

4. Verify Creation

Get all todos to see the new one:

curl http://localhost:3000/todos

Response should now include the new todo:

{
  "success": true,
  "data": [
    {
      "id": "1",
      "title": "Learn NestJS",
      ...
    },
    {
      "id": "2",
      "title": "Build RESTful API",
      ...
    },
    {
      "id": "1705312200000",
      "title": "New Todo",
      "description": "Learn NestJS",
      "completed": false,
      ...
    }
  ]
}

📝 Summary

POST Request Flow Summary:

  1. ✅ HTTP POST request arrives with JSON body
  2. ✅ Express.js receives and parses JSON body
  3. ✅ NestJS router matches POST /todos to TodosController.create()
  4. @Body() decorator extracts and maps body to CreateTodoDto
  5. ✅ NestJS injects TodosService into controller
  6. ✅ Controller calls this.todosService.create(createTodoDto)
  7. ✅ Service generates ID, creates todo object, adds to array
  8. ✅ Service returns created todo
  9. ✅ Controller wraps todo in response object
  10. @HttpCode(HttpStatus.CREATED) sets status to 201
  11. ✅ NestJS serializes to JSON (converts Date to ISO string)
  12. ✅ HTTP 201 response sent with created todo in body

Total Execution Time: Typically < 10ms for in-memory operations

Key Differences from GET: - ✅ POST has request body (data to create) - ✅ @Body() decorator extracts body data - ✅ DTO defines expected structure - ✅ Service creates new resource - ✅ Returns 201 Created (not 200 OK) - ✅ Response includes created resource


🎓 Learning Points

  1. POST Method - Used for creating new resources
  2. Request Body - Data sent in POST requests
  3. @Body() Decorator - Extracts request body
  4. DTOs - Define structure of incoming data
  5. HTTP Status Codes - 201 for successful creation
  6. ID Generation - How to create unique identifiers
  7. Data Storage - Adding data to in-memory array
  8. Response Format - Including created resource in response

POST Request Flow:

Client sends POST with JSON body
    ↓
Server parses JSON
    ↓
@Body() extracts data
    ↓
Service creates resource
    ↓
Server returns 201 with created resource

This demonstrates how NestJS handles data creation: you define the structure with a DTO, extract it with @Body(), process it in a service, and NestJS handles all the HTTP details automatically!


🔄 Comparison: GET vs POST

Aspect GET /todos POST /todos
Purpose Retrieve data Create data
Request Body None JSON with todo data
Method @Get() @Post()
Status Code 200 OK 201 Created
Idempotent Yes No
Service Method findAll() create()
Returns Array of todos Created todo
Side Effect None (read-only) Adds new todo to array

Both requests: - Use the same controller (TodosController) - Use the same service (TodosService) - Follow the same dependency injection pattern - Return JSON responses - Use the same route base (/todos)

The main difference: POST creates new data, GET retrieves existing data!