NestJS Backend

Building a Production NestJS API:
PostgreSQL, Redis & Docker

By Ankit Kumar, Founder of Kyvronix Technologies · 20 min read

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