From an empty terminal to a working custom endpoint: installing the Nest CLI, reading the generated project, and how the controller and service split the work between them.

Ever heard of NestJS and wondered why so many developers, especially from the Node.js world, are flocking to it? In short, NestJS brings structure and order to backend development with TypeScript. If you’re looking to get started, this article is your step-by-step guide to building your very first REST API.
We’ll go from installation all the way to creating our own custom endpoint. Let’s dive right in!
One note before we start: this guide targets NestJS 12, the current release line (published August 2026). The decorators and CLI commands below are unchanged from earlier versions — the Node.js requirement is the part that moved.
Before we start coding, it’s good to know what makes NestJS so special:
Enough with the introductions, let’s get our tools ready!
Make sure you have these things installed on your computer:
All set? Let’s install the NestJS CLI.
NestJS has a super helpful Command Line Interface (CLI) for quickly scaffolding projects, modules, controllers, and other components. Open your terminal and run this command:
npm install -g @nestjs/cliThe command above will install the NestJS CLI globally on your system.
Once the CLI is installed, creating a new project is a piece of cake. Navigate to your desired working directory, then run:
nest new my-first-apiThe CLI will ask you which package manager you’d like to use (npm, yarn, or pnpm). Choose one, and let the CLI work its magic creating the project structure for you.
Once it’s done, navigate into your project directory:
cd my-first-apiIf you open the project folder, you’ll see a structure like this:
src/
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.tsWithout changing a single thing, we already have a runnable application. To run it in development mode (with hot-reload), use this command:
npm run start:devWait a moment, and you’ll see a log in the terminal indicating that the application is running, usually on port 3000.
Now, open your browser or Postman and access http://localhost:3000. You'll be greeted with the message: Hello World!
Congratulations! Your first API is up and running! 🎉
Where did that “Hello World!” come from? Let’s break down the flow.
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}The @Controller() decorator marks this class as a controller. The @Get() decorator on the getHello() method means this method will handle GET requests to the root path (/).
3. Service Does the Work: The controller doesn’t just provide the answer directly. It calls the getHello() method from the AppService. This is a best practice to separate routing logic from business logic.
// src/app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}The @Injectable() decorator marks this class so it can be injected into other components (like our controller). This is where "Hello World!" is actually generated.
Now, let’s create something more interesting. We’ll build a new endpoint GET /greeting/:name that will provide a personalized greeting.
1. Modify the Service (app.service.ts)
Add a new method to create the greeting message.
// src/app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
// Add this new method
getGreeting(name: string): string {
return `Hello, ${name}! Welcome to your first API.`;
}
}2. Modify the Controller (app.controller.ts)
Add a new method in the controller to create the route and call the new service method we just made.
// src/app.controller.ts
import { Controller, Get, Param } from '@nestjs/common'; // Don't forget to import Param
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
// Add this new method
@Get('greeting/:name')
sendGreeting(@Param('name') name: string): string {
return this.appService.getGreeting(name);
}
}Notice:
3. Test It!
Save all your changes. The development server will automatically restart. Now, open your browser or Postman and access our new URL, for example:
http://localhost:3000/greeting/Budi
You will get the following response:
Hello, Budi! Welcome to your first API.Try replacing “Budi” with your own name and see the result!
Congratulations! You have successfully created a simple REST API project with NestJS, understood its basic components (Controller, Service, Module), and even created a custom endpoint with parameters.
This is just the tip of the iceberg of what NestJS can do. The next steps you could explore are:
Keep experimenting and happy coding! 👨💻👩💻

Why the default flat src/ falls apart once a project has real features, and the module-driven layout that replaces it — one directory per feature, with common, config and database kept alongside.

Building a type-safe registration form in Next.js with React Hook Form and a Zod schema — setup, validation, and wiring the component into a page, step by step.