Welcome to NestJS! If you are coming from Express or Fastify, NestJS can feel a bit rigid at first because it heavily embraces Architecture and Dependency Injection (very similar to Angular or Spring Boot). However, once you get the hang of it, that rigidity becomes its superpower—it ensures large codebases stay organized.
When you create a new project using nest new project-name, you get a boilerplate. NestJS relies heavily on a module-based (domain-driven) architecture.
Here is what a standard, well-organized NestJS project structure looks like as it grows:
src/
├── app.module.ts # The root module of the application
├── main.ts # The entry point (starts the HTTP server)
│
├── users/ # A dedicated feature/domain folder (e.g., "Users")
│ ├── users.module.ts # Ties the controllers and services together
│ ├── users.controller.ts # Handles incoming HTTP requests (routing, params)
│ ├── users.service.ts # Handles business logic and DB queries
│ │
│ ├── dto/ # Data Transfer Objects (request validation shapes)
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ │
│ └── entities/ # Database models/schemas
│ └── user.entity.ts
- Modules (
.module.ts): The glue. Every feature needs a module that encapsulates its controllers and services. YourAppModuleimports all these sub-modules. - Controllers (
.controller.ts): The front door. They handle incoming HTTP requests (likeGET /usersorPOST /users) and hand off the heavy lifting to services. - Services (
.service.ts): The brain. This is where your business logic, database queries, and data processing live.
NestJS doesn't force a specific database tool on you, but the most common, idiomatic approach is using TypeORM (Object Relational Mapper) or Prisma. Let’s look at how to connect using TypeORM, which is deeply integrated into Nest's ecosystem.
Run this in your terminal to install Nest's TypeORM wrapper, TypeORM itself, and the Postgres driver (pg):
npm install @nestjs/typeorm typeorm pg
You establish the connection in your root app.module.ts using TypeOrmModule.forRoot().
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { User } from './users/entities/user.entity';
@Module({
imports: [
// 1. Establish the global DB connection
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your_db_user',
password: 'your_db_password',
database: 'your_db_name',
entities: [User], // Tell TypeORM which database models to look at
synchronize: true, // Automatically creates/updates DB tables (Turn off in production!)
}),
// 2. Import your feature modules
UsersModule,
],
})
export class AppModule {}An entity maps directly to a PostgreSQL table using decorators.
// src/users/entities/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users') // Map this class to the 'users' table in Postgres
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ unique: true })
email: string;
}To actually interact with the database inside your code, you register the entity in your feature module and inject it into your service.
First, register it in the feature module:
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])], // Makes the User repository available here
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}Now, you can use the Repository Pattern inside your service to run Postgres queries automatically:
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>, // Nest injects the Postgres client here
) {}
// Get all users from Postgres
findAll(): Promise<User[]> {
return this.usersRepository.find();
}
// Create a new user in Postgres
async create(userData: Partial<User>): Promise<User> {
const newUser = this.usersRepository.create(userData);
return this.usersRepository.save(newUser);
}
}Are you looking to use TypeORM, or were you planning on using an alternative ORM like Prisma or a raw SQL query builder like Kysely or Knex?