Lesson 14 of 49 intermediate

API Design & REST

Building Roads for Your Data

Open interactive version (quiz + challenge)

Real-world analogy

An API is like a restaurant menu. The customer (frontend) looks at the menu (API docs) and orders (makes a request). The kitchen (backend) prepares the food (data) and serves it. The customer never goes INTO the kitchen — they just use the menu!

What is it?

REST (Representational State Transfer) is an architectural style for designing APIs. It uses HTTP methods and URLs to represent resources and actions. Combined with proper status codes and DTOs, it creates predictable, developer-friendly APIs.

Real-world relevance

Twitter, GitHub, Stripe, and virtually every modern web service exposes a REST API. Stripe's API is often cited as the gold standard of API design.

Key points

Code example

// RESTful routes — clean and predictable 📐
@Controller('products')
export class ProductController {
  @Get()              // GET    /products      → list all
  findAll(@Query() query: FilterDto) {
    return this.productService.findAll(query);
  }

  @Get(':id')         // GET    /products/123  → get one
  findOne(@Param('id') id: string) {
    return this.productService.findOne(id);
  }

  @Post()             // POST   /products      → create
  create(@Body() dto: CreateProductDto) {
    return this.productService.create(dto);
  }

  @Put(':id')         // PUT    /products/123  → update
  update(@Param('id') id: string, @Body() dto: UpdateProductDto) {
    return this.productService.update(id, dto);
  }

  @Delete(':id')      // DELETE /products/123  → delete
  remove(@Param('id') id: string) {
    return this.productService.remove(id);
  }
}

// DTO with validation — reject bad data at the door! 🚫
class CreateProductDto {
  @IsString()
  @MinLength(2)
  name: string;

  @IsNumber()
  @Min(0)
  price: number;

  @IsOptional()
  @IsString()
  description?: string;
}

// Swagger — auto-generated docs! 📚
@ApiTags('Products')
@ApiOperation({ summary: 'Create a new product' })
@ApiResponse({ status: 201, description: 'Product created' })
@ApiResponse({ status: 400, description: 'Invalid data' })
@Post()
create(@Body() dto: CreateProductDto) { ... }

Line-by-line walkthrough

  1. 1. RESTful routes — clean and predictable 📐
  2. 2. Decorator that adds metadata or behavior
  3. 3. Exporting for use in other files
  4. 4. Decorator that adds metadata or behavior
  5. 5.
  6. 6. Returning a value
  7. 7. Closing block
  8. 8.
  9. 9. Decorator that adds metadata or behavior
  10. 10.
  11. 11. Returning a value
  12. 12. Closing block
  13. 13.
  14. 14. Decorator that adds metadata or behavior
  15. 15.
  16. 16. Returning a value
  17. 17. Closing block
  18. 18.
  19. 19. Decorator that adds metadata or behavior
  20. 20.
  21. 21. Returning a value
  22. 22. Closing block
  23. 23.
  24. 24. Decorator that adds metadata or behavior
  25. 25.
  26. 26. Returning a value
  27. 27. Closing block
  28. 28. Closing block
  29. 29.
  30. 30. DTO with validation — reject bad data at the door! 🚫
  31. 31. Declaring a class
  32. 32. Decorator that adds metadata or behavior
  33. 33. Decorator that adds metadata or behavior
  34. 34.
  35. 35.
  36. 36. Decorator that adds metadata or behavior
  37. 37. Decorator that adds metadata or behavior
  38. 38.
  39. 39.
  40. 40. Decorator that adds metadata or behavior
  41. 41. Decorator that adds metadata or behavior
  42. 42.
  43. 43. Closing block
  44. 44.
  45. 45. Swagger — auto-generated docs! 📚
  46. 46. Decorator that adds metadata or behavior
  47. 47. Decorator that adds metadata or behavior
  48. 48. Decorator that adds metadata or behavior
  49. 49. Decorator that adds metadata or behavior
  50. 50. Decorator that adds metadata or behavior
  51. 51.

Spot the bug

@Controller('users')
export class UserController {
  @Post(':id')
  deleteUser(@Param('id') id: string) {
    return this.userService.delete(id);
  }
}
Need a hint?
Does the HTTP method match the action being performed?
Show answer
The method uses @Post() but deletes a user. POST is for creating, DELETE is for deleting. Fix: change @Post(':id') to @Delete(':id').

Explain like I'm 5

An API is like a restaurant menu. You look at the menu and tell the waiter 'I want chicken' (make a request). The kitchen makes it and the waiter brings it back. You never go into the kitchen yourself - you just use the menu to order!

Fun fact

The GitHub API has over 1,000 endpoints. Stripe's API documentation is so good that it's used as a teaching example in university computer science courses! 📚

Hands-on challenge

Design a REST API for a 'bookstore' on paper first: what endpoints would you need for books, authors, and orders? Then implement the books CRUD in NestJS!

More resources

Open interactive version (quiz + challenge) ← Back to course: Full-Stack Playbook