NestJS is arguably the best Node.js framework for building scalable, enterprise-grade backends. Its opinionated architecture (heavily inspired by Angular) forces you to write clean, modular, and testable code. When we built the backend for BhuMitra, we knew we needed a robust stack to handle real-time WebSockets and complex spatial queries. We chose NestJS, PostgreSQL, and Redis.
In this guide, I will walk you through setting up this exact trio in a production-ready Dockerized environment.
1. Project Initialization
First, install the Nest CLI and generate a new project:
npm i -g @nestjs/cli nest new bhumitra-api cd bhumitra-api
2. The Docker Compose Stack
Before writing application logic, we need our infrastructure. We don't want to install Postgres and Redis directly on our host machines. Let's create a docker-compose.yml file in the root directory.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: bhumitra_db
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Run docker-compose up -d to start your database and cache layers in the background.
3. Configuring TypeORM and PostgreSQL
NestJS has excellent support for TypeORM. Let's install the dependencies:
npm install @nestjs/typeorm typeorm pg
Now, configure the TypeOrmModule in your AppModule (preferably using ConfigModule for environment variables, but we'll hardcode for brevity here):
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'devuser',
password: 'devpassword',
database: 'bhumitra_db',
autoLoadEntities: true,
synchronize: true, // IMPORTANT: Set to false in production! Use migrations.
}),
],
})
export class AppModule {}
4. Integrating Redis for Caching
To prevent hitting the database for frequently requested, rarely changing data, we integrate Redis using the cache-manager package.
npm install @nestjs/cache-manager cache-manager cache-manager-redis-store
Configure it in the AppModule:
import { CacheModule } from '@nestjs/cache-manager';
import * as redisStore from 'cache-manager-redis-store';
@Module({
imports: [
// ... TypeOrmModule ...
CacheModule.register({
isGlobal: true,
store: redisStore,
host: 'localhost',
port: 6379,
}),
],
})
export class AppModule {}
5. Using the Cache in a Service
Now, you can inject the CACHE_MANAGER into any service to drastically improve performance.
import { Injectable, Inject } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
@Injectable()
export class UsersService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
async getUserProfile(id: string) {
const cacheKey = `user_profile_${id}`;
// Check Redis first
const cachedData = await this.cacheManager.get(cacheKey);
if (cachedData) return cachedData;
// If not in cache, hit Postgres (simulate with setTimeout)
const dbData = { id, name: 'Ankit Kumar' }; // await repository.findOne(...)
// Store in Redis for 1 hour (3600 seconds)
await this.cacheManager.set(cacheKey, dbData, 3600);
return dbData;
}
}
Conclusion
By defining your infrastructure in Docker Compose and using NestJS's modular architecture, you create a backend that is pleasant to develop locally and incredibly robust in production. This architecture easily handles tens of thousands of concurrent requests when properly scaled.
— Ankit Kumar