If you already know C#/.NET and Angular, you’re most of the way to NestJS — you just haven’t seen it framed that way yet. Dependency injection, decorators, modules: NestJS takes the patterns you already use every day and arranges them in Node and TypeScript.
So instead of “learn NestJS from scratch,” this tutorial maps what you already know onto a real build. We go from an empty project to a complete REST API — a Bookmark / Link Manager with five working CRUD endpoints.
📺 Watch the full walkthrough on YouTube (~22 min)
Why NestJS feels familiar
Most “intro to NestJS” content assumes you’re starting from zero. You’re not. Here’s how the core ideas map over:
| You already know (C# / Angular) | In NestJS |
|---|---|
| Dependency injection via the constructor | The same — providers injected through the constructor |
Decorators ([HttpGet], Angular @Component) | @Controller, @Get, @Injectable |
| Modules / assemblies | @Module — feature modules wiring providers together |
| Model validation / data annotations | DTOs with class-validator and a global ValidationPipe |
Once you see the mapping, the framework stops feeling foreign and starts feeling like home with a different accent.
What we build
A Bookmark / Link Manager API. The stack:
- NestJS 11
- Prisma + SQLite — zero-config database, so the focus stays on NestJS
- class-validator +
PartialTypefor DTOs - HTTPie for a live demo
The five endpoints:
| Method | Route | Description |
|---|---|---|
POST | /bookmarks | Create a bookmark |
GET | /bookmarks | List bookmarks (filter with ?tag=) |
GET | /bookmarks/:id | Get one |
PATCH | /bookmarks/:id | Update |
DELETE | /bookmarks/:id | Delete |
Validation that mirrors what you know from .NET
If you’ve used data annotations and model validation in ASP.NET, DTOs in NestJS will feel immediately familiar. You describe the shape with decorators:
export class CreateBookmarkDto {
@IsString()
@IsNotEmpty()
title: string;
@IsUrl()
url: string;
@IsArray()
@IsString({ each: true })
tags: string[];
}
The update DTO is just the create DTO with everything optional — PartialType gives you that for free:
export class UpdateBookmarkDto extends PartialType(CreateBookmarkDto) {}
Then a global ValidationPipe enforces it everywhere:
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
whitelist strips unknown properties, forbidNonWhitelisted rejects requests that send them, and transform hands your controllers real DTO instances. Clean inputs, no manual checks.
One deliberate shortcut
SQLite has no native array type, so the bookmark’s tags are stored as a JSON-encoded string. It’s a deliberate shortcut — and a setup for a later post, where we migrate to Postgres and do tags (and search) properly.
What’s next in the series
This is the foundation. From here:
- Auth — protecting the API.
- A Postgres migration — undoing the SQLite tags shortcut the right way.
- The big one — this API gets a brain. Paste a URL and an LLM auto-summarizes the page and writes the tags, plus semantic search across your whole library: ask your bookmarks in plain English and get the right ones back.
Code & video
- 📺 Watch the full tutorial
- 💻 Source code on GitHub
- Subscribe to The Coderlog for the rest of the series.