This episode walks you through creating your first NestJS project with the Nest CLI, understanding the src/app.module.ts and main.ts structure, running the application, checking the basic endpoint, and applying initial configuration with @nestjs/config.

Enough architecture theory — it's time to get your hands on the keyboard. Episode 3 walks you through creating your first NestJS project from scratch with the Nest CLI, understanding each generated file, running the server, and adding basic configuration.
By the end of this episode you'll have a running NestJS application and understand the default structure that forms the foundation of every NestJS project in this series.
The Nest CLI provides the nest new command to create a complete project with a standard structure:
nest new belajar-nestjsThe Nest CLI will ask for a package manager — choose npm. When it's done, enter the project folder and run the development server:
cd belajar-nestjs
npm run start:devThe npm run start:dev command runs the application in watch mode — every file change will automatically restart the server.
Open your browser to http://localhost:3000. You'll see the text Hello World! returned by the default controller:
curl http://localhost:3000The Hello World! response means the application is running well. This is the response from AppController, located in the src folder.
main.ts is the application's entry point. This is where NestFactory.create is called and the application is bootstrapped. All global setup like validation pipes and CORS will be done in this file later.
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);
}
void bootstrap();Notice process.env.PORT ?? 3000 — the application reads the port from an environment variable with a fallback to 3000.
app.module.ts is the root module. All feature modules you create later will be registered via the imports array in this file.
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}This structure shows the core NestJS pattern: a module declares the controller that handles requests and the provider that contains the logic.
For development, npm run start:dev gives you watch mode. To run once without watching, use npm run start. When you're ready for production, npm run build compiles TypeScript into the dist folder, then npm run start:prod runs the compiled output.
npm run build
npm run start:prodWhile the server is running, NestJS displays logs in the terminal — including contexts like NestApplication and the Application is running on message. These logs help you make sure modules and providers are registered correctly.
To manage environment variables in a structured way, install @nestjs/config:
npm install @nestjs/configRegister ConfigModule in app.module.ts:
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
@Module({
imports: [ConfigModule.forRoot()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}ConfigModule.forRoot() reads the .env file in the project root and exposes its values through ConfigService.
Create a .env file in the project root:
PORT=3000
NODE_ENV=developmentDon't forget to add .env to .gitignore so secrets don't get committed. ConfigService will be used to read these values later — we'll cover the full details in episode 8.
Episode 3 successfully takes you from zero to your first running NestJS application. You now understand nest new, the main.ts and app.module.ts structure, development and production modes, and the first steps of configuration with @nestjs/config.
Key takeaways:
nest new creates a complete project; choose npm as the package manager.npm run start:dev runs the server with watch mode.main.ts is the entry point; app.module.ts is the root module.curl http://localhost:3000 verifies the application is running.npm run build compiles to dist for production.ConfigModule.forRoot() enables reading the .env file.In the next episode 4 we'll discuss controllers and routing — creating controllers and route handlers, using the @Get @Post @Put @Delete @Patch decorators, handling route parameters, query parameters, body parsing, and setting status codes and response serialization.