TypeScript and Decorators in NestJS Explained Simply

TypeScript and Decorators in NestJS Explained Simply

If you have looked at NestJS code for the first time, there is a good chance your brain had two immediate reactions:

  1. Why is there so much TypeScript?
  2. Why is everything wearing an @ sign?

Totally fair.

NestJS looks clean, but when you first see things like:

  • @Module()
  • @Controller()
  • @Injectable()
  • @Get()

it can feel like the framework is speaking a very confident language you have not fully learned yet.

The good news is this:

once you understand TypeScript and decorators, NestJS starts to feel much more readable and much less mysterious.

And honestly, these two things are a huge part of why NestJS feels so structured.

So in this article, we are going to make both of them simple.

By the end, you will understand:

  • why NestJS works so well with TypeScript
  • what decorators actually are
  • what common NestJS decorators do
  • why they make code easier to read and scale
  • what beginners usually misunderstand at first

Let’s make the @ signs less scary.


Why TypeScript Feels So Natural in NestJS

NestJS can be used with JavaScript, but it is clearly designed to shine with TypeScript. Nest officially says it is built with and fully supports TypeScript, while still allowing plain JavaScript projects. :contentReference[oaicite:1]{index=1}

That is not just a marketing detail.

It affects the whole development experience.

TypeScript adds:

  • types
  • interfaces
  • better autocomplete
  • safer refactoring
  • clearer contracts between parts of your app

For a backend framework, that matters a lot.

Because backend code usually has to deal with:

  • request bodies
  • response shapes
  • DTOs
  • services calling services
  • configuration
  • business rules
  • code that gets touched again later by tired humans

JavaScript is flexible, which is great.

But when a project starts growing, TypeScript often makes the codebase easier to reason about.

That is one of the reasons NestJS feels so “organized” compared to more free-form Node.js setups.


Why TypeScript Helps Beginners More Than It Hurts

Some beginners hear “TypeScript” and immediately think:

“Cool, one more thing to be confused about.”

Reasonable reaction.

But in practice, TypeScript can actually help beginners because it makes mistakes more visible.

For example, if your function expects a number and you pass a string, TypeScript can warn you before the bug becomes a small production mystery.

That means:

  • fewer silent mistakes
  • clearer code intentions
  • easier understanding of what data is supposed to look like

So while TypeScript adds a bit of learning upfront, it often reduces confusion later.

That is a very good trade.


A Tiny TypeScript Example

Here is a super basic JavaScript-style function:

function greet(name) {
  return `Hello, ${name}`;
}

This works.

But TypeScript makes it clearer:

function greet(name: string): string {
  return `Hello, ${name}`;
}

Now we know:

  • name should be a string
  • the function returns a string

That may seem small, but this kind of clarity adds up fast in real apps.

And NestJS is full of places where that clarity helps:

  • controller methods
  • service methods
  • DTOs
  • config values
  • injected providers

So... What Is a Decorator?

Now for the part that makes NestJS look fancy.

A decorator is a special kind of declaration that can be attached to a class, method, property, accessor, or parameter, using the @something syntax. That is how the TypeScript handbook still describes them.

In plain English:

a decorator adds metadata or behavior to something in your code.

It helps tell the framework what that thing is supposed to mean.

For example:

@Controller('users')
export class UsersController {}

That @Controller('users') decorator tells Nest:

“Hey, this class is a controller, and it handles routes under /users.”

That is why decorators are so important in NestJS.

They make the code expressive.

Instead of writing long configuration objects somewhere else, you put meaning directly on the class or method.

That makes the code easier to scan and understand.


Why NestJS Uses Decorators So Much

NestJS relies heavily on decorators because they help define the structure of the application in a very readable way.

Instead of writing a bunch of framework registration code manually, you decorate classes and methods to describe their role.

For example:

  • @Module() tells Nest a class is a module
  • @Controller() tells Nest a class is a controller
  • @Injectable() tells Nest a class can be injected
  • @Get() tells Nest a method handles GET requests

That is much cleaner than burying the meaning in separate config files.

Nest’s docs describe @Module() as the decorator that provides metadata Nest uses to organize the application structure efficiently, and controller docs still describe controllers as the classes that handle incoming requests and send responses.

So yes, decorators are not there to make the code look cool.

They are there to make the architecture readable.

Very different energy.


The Most Common NestJS Decorators You Will See

Let’s break down the decorators beginners see first.

@Module()

This marks a class as a module.

Example:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

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

What it means:

  • this class defines a Nest module
  • here are the controllers in it
  • here are the providers in it

Nest uses this metadata to understand how the app is organized.


@Controller()

This marks a class as a controller.

Example:

import { Controller, Get } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return ['Alice', 'Bob'];
  }
}

What it means:

  • this class handles requests
  • its routes start with /users

Nest’s controller docs still define controllers as responsible for handling incoming requests and sending responses.


@Injectable()

This marks a class as a provider that can participate in dependency injection.

Example:

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  findAll() {
    return ['Alice', 'Bob'];
  }
}

What it means:

  • this class can be managed by Nest
  • it can be injected into controllers or other providers

This is a big part of why NestJS dependency injection works so nicely.


Route Decorators like @Get(), @Post(), @Put(), @Delete()

These attach HTTP behavior to controller methods.

Example:

import { Controller, Get, Post } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return ['Alice', 'Bob'];
  }

  @Post()
  create() {
    return { message: 'User created' };
  }
}

What it means:

  • findAll() handles GET requests
  • create() handles POST requests

This is one of the reasons Nest controllers feel so readable.

You can almost understand the API just by scanning the decorators.


Parameter Decorators like @Body(), @Param(), and @Query()

Nest also provides param decorators that map parts of the HTTP request to method parameters, such as @Param(), @Body(), and @Query(). The custom decorators docs still list these built-in param decorators and what they correspond to on the request object.

Example:

import { Controller, Get, Param, Post, Body } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    return { id };
  }

  @Post()
  create(@Body() body: any) {
    return body;
  }
}

What it means:

  • @Param('id') gets the route parameter
  • @Body() gets the request body

Again, very readable.


How TypeScript and Decorators Work Together in NestJS

This is where NestJS really starts to feel elegant.

TypeScript gives you:

  • type safety
  • class syntax
  • interfaces
  • metadata-friendly structure

Decorators give you:

  • expressive framework annotations
  • clear intent on classes and methods
  • readable architecture

Together, they make code like this possible:

import { Controller, Get, Injectable, Module } from '@nestjs/common';

@Injectable()
export class AppService {
  getMessage(): string {
    return 'Hello from NestJS';
  }
}

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getMessage();
  }
}

@Module({
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

This little example already tells you a lot:

  • what is a module
  • what is a controller
  • what is injectable
  • what route exists
  • what types come back

That is the NestJS style in a nutshell.

Clear roles.
Readable code.
Less guessing.


Why Decorators Make NestJS Code Easier to Read

Some people look at decorators and think they are just extra syntax.

But in NestJS, they do something really useful:

they make meaning visible right where you need it

Without decorators, a lot of framework wiring would live elsewhere.

With decorators, the code tells you more immediately.

You can look at a class and quickly see:

  • what kind of class it is
  • what route it belongs to
  • whether it can be injected
  • how a method behaves
  • where data comes from

That is especially helpful on teams and in larger codebases.

Because nobody enjoys opening a file and playing “guess what this class does.”


Custom Decorators Exist Too

Nest does not stop at built-in decorators.

The docs also show how you can create custom decorators, especially param decorators, using createParamDecorator. A common example is a @User() decorator that pulls request.user from the current request.

Example idea:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (_data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

Then use it like:

@Get()
findMe(@User() user: any) {
  return user;
}

That is very cool later on.

But for now, beginners mostly just need to understand that:

  • decorators are not limited to built-in ones
  • Nest lets you create your own readable abstractions too

Common Beginner Confusion About Decorators

Let’s clear up a few things.

“Are decorators just functions?”

In practice, they are expressions that evaluate to functions and are applied at runtime with information about the decorated declaration, which is how the TypeScript docs describe them.

You do not need to deeply master the internals on day one.

You just need to understand that decorators help attach meaning and metadata to your code.


“Do decorators replace business logic?”

No.

Decorators describe or configure behavior.
They do not replace actual application logic.

Your service still does the real work.
Your controller still handles the request.

The decorator helps Nest understand what role that code plays.


“Do I need to create my own decorators right away?”

Not at all.

Start with the built-in ones:

  • @Module()
  • @Controller()
  • @Injectable()
  • @Get()
  • @Post()
  • @Param()
  • @Body()
  • @Query()

That is more than enough for now.


“Can I learn NestJS without loving TypeScript?”

Yes.

But you will probably understand NestJS better if you get comfortable with basic TypeScript.

You do not need to know everything.

But knowing:

  • types
  • classes
  • interfaces
  • constructor injection
  • imports/exports

will help a lot.


What Beginners Really Need to Remember

You do not need a PhD in TypeScript wizardry to start using NestJS.

What you do need is this simple mental model:

TypeScript helps make your code safer and clearer

It gives structure to the code.

Decorators help make your code expressive

They tell Nest what classes and methods are supposed to do.

That is the combo.

TypeScript gives clarity.
Decorators give meaning.
NestJS uses both to create a backend style that feels organized and readable.


Why This Matters More Later

When your project is tiny, it is easy to underestimate how useful this is.

But as your codebase grows, TypeScript and decorators make it much easier to:

  • understand feature boundaries
  • refactor safely
  • onboard new developers
  • keep routes and providers readable
  • avoid messy implicit behavior

This is one of those things that starts as “a bit unfamiliar” and later becomes “I really do not want to go back.”

That is usually a good sign.


Final Thoughts

TypeScript and decorators are a huge part of why NestJS feels the way it does.

TypeScript gives you stronger structure, clearer contracts, and safer code.

Decorators make the code expressive by showing what classes and methods are supposed to do right where you read them. The TypeScript handbook defines decorators broadly, and Nest builds many of its core APIs on that pattern.

That is why NestJS often feels so clean.

It is not only because the framework is well designed.

It is also because the syntax helps communicate architecture directly in the code.

So if decorators looked strange at first, that is normal.

But once they click, they stop looking weird and start looking useful.

And now that you understand the basic building blocks, dependency injection, TypeScript, and decorators, the next step is learning how a request actually moves through a NestJS app.

Because that is where things like:

  • middleware
  • guards
  • pipes
  • interceptors
  • exception filters

start to make real sense.

Now we will talk about the NestJS request lifecycle in the next chapter


Real Interview Questions

What are decorators in NestJS?

Decorators in NestJS are special annotations like @Controller() and @Injectable() that help describe what a class or method does so Nest can wire the application correctly.

Why does NestJS use TypeScript so heavily?

NestJS is built with and fully supports TypeScript, and TypeScript helps make backend code safer, clearer, and easier to maintain in larger projects.

Do I need to know advanced TypeScript before learning NestJS?

No. Basic TypeScript knowledge is enough to get started.

What does @Controller() do in NestJS?

It marks a class as a controller responsible for handling incoming requests.

Can I create my own decorators in NestJS?

Yes. Nest supports custom decorators, including custom parameter decorators using createParamDecorator.

Subscribe for new post. No spam, just tech.