NestJS for C# and Angular Developers: Build a REST API from Scratch


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 constructorThe same — providers injected through the constructor
Decorators ([HttpGet], Angular @Component)@Controller, @Get, @Injectable
Modules / assemblies@Module — feature modules wiring providers together
Model validation / data annotationsDTOs 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 + PartialType for DTOs
  • HTTPie for a live demo

The five endpoints:

MethodRouteDescription
POST/bookmarksCreate a bookmark
GET/bookmarksList bookmarks (filter with ?tag=)
GET/bookmarks/:idGet one
PATCH/bookmarks/:idUpdate
DELETE/bookmarks/:idDelete

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:

  1. Auth — protecting the API.
  2. A Postgres migration — undoing the SQLite tags shortcut the right way.
  3. 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top