System Architecture

Architecture and Technologies

Understand how technologies relate, the request flow, caching, and database access.

Technology Stack

Framework

  • NestJS 11.x
  • TypeScript
  • Node.js

Databases

  • MongoDB (Mongoose + pool)
  • PostgreSQL (TypeORM + pool)

Cache & Messaging

  • Redis (ioredis)
  • Apache Kafka

Storage & APIs

  • MinIO (S3 Compatible)
  • GraphQL (Apollo)
  • REST API

Authentication

  • JWT (Passport)
  • Argon2id
  • Social login
  • Active Directory

Services

  • Graylog (GELF Logging)
  • SMTP / MailHog (Email)
  • WhatsApp (Evolution API)
  • Terminus (/health)

Configuration and Environment Variables

All configuration is centralized through config files in src/config/ and accessed via ConfigService. Environment variables are loaded automatically.

Config Files

  • app.config.ts - Application settings
  • database.config.ts - MongoDB and PostgreSQL (includes pool)
  • redis.config.ts - Redis cache
  • kafka.config.ts - Kafka messaging
  • jwt.config.ts - JWT authentication
  • argon2.config.ts - Password hashing
  • google-oauth.config.ts - Google OAuth2
  • facebook-oauth.config.ts - Facebook OAuth2
  • twitter-oauth.config.ts - X / Twitter OAuth2 (PKCE)
  • github-oauth.config.ts - GitHub OAuth2
  • figma-oauth.config.ts - Figma OAuth2
  • linkedin-oauth.config.ts - LinkedIn OpenID Connect
  • slack-oauth.config.ts - Slack OpenID Connect
  • atlassian-oauth.config.ts - Atlassian OAuth 2.0 (3LO)
  • gitlab-oauth.config.ts - GitLab OAuth2
  • bitbucket-oauth.config.ts - Bitbucket OAuth2
  • discord-oauth.config.ts - Discord OAuth2
  • twitch-oauth.config.ts - Twitch OAuth2
  • steam-openid.config.ts - Steam OpenID 2.0
  • reddit-oauth.config.ts - Reddit OAuth2
  • amazon-oauth.config.ts - Amazon Login with Amazon
  • patreon-oauth.config.ts - Patreon OAuth2
  • dropbox-oauth.config.ts - Dropbox OAuth2
  • metamask-auth.config.ts - MetaMask SIWE (EIP-4361)
  • ad-ldap.config.ts - Active Directory LDAP/LDAPS (on-prem AD DS)
  • apple-oauth.config.ts - Apple Sign In (OIDC form_post)
  • smtp.config.ts - SMTP email
  • minio.config.ts - MinIO storage
  • graylog.config.ts - Graylog logging (GELF)
  • graphql.config.ts - GraphQL
  • whatsapp.config.ts - WhatsApp API

Usage Example

TypeScriptConfigService
constructor(private configService: ConfigService) {}

// Access configuration
const mongoUri = this.configService.get('database.mongoUri');
const redisHost = this.configService.get('redis.host');
const jwtSecret = this.configService.get('jwt.secret');

Request Flow

1. Client sends HTTP request

Request arrives at the NestJS server with headers (Authorization, Content-Type, etc.)

2. Security (IP / catch-all)

SecurityModule tracks invalid API routes and can block IPs (catch-all in production/staging). Admin API under /api/v1/security — see the Security page.

3. JWT Authentication

JwtAuthGuard validates the JWT token. Routes marked with @Public() are skipped.

4. Role Authorization

RolesGuard checks whether the user has the required roles defined via @Roles()

5. Cache Interceptor

TypeOrmCacheInterceptor or MongooseCacheInterceptor checks for cached data in Redis

6. Controller/Resolver

Request reaches the controller (REST) or resolver (GraphQL)

7. Service Layer

Business logic runs in the service, which uses repositories for data access

8. Repository Pattern

The module service injects the chosen repository — PostgreSQL (TypeORM) and/or MongoDB (Mongoose) — based on domain needs. Two repositories per domain allow a single database or CQRS (commands in one, queries in the other).

9. Logging (Graylog)

Interceptor and exception filter send structured GELF logs to Graylog (when GRAYLOG_ENABLED=true).

10. Response and Cache

Data is returned to the client and, when applicable, stored in Redis for subsequent requests

Cache System

How It Works

The cache system uses Redis as an intermediate layer to store results from frequent queries, reducing database load and improving response time.

Cache Interceptors

  • TypeOrmCacheInterceptor - Cache for TypeORM queries (PostgreSQL)
  • MongooseCacheInterceptor - Cache for Mongoose queries (MongoDB)
  • CacheService - Centralized service for cache management

Redis Configuration

EnvironmentRedis Config
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_TTL=3600  # Time to Live in seconds

Cache Flow

  1. Request reaches the cache interceptor
  2. Interceptor checks if a key exists in Redis
  3. If it exists: return cached data (cache hit)
  4. If not: run the database query, store in Redis, and return (cache miss)
  5. Data expires automatically after the configured TTL

Persistence: Mongo, Postgres, and CQRS-ready

Template pattern

The template keeps MongoDB and PostgreSQL in the stack. Each domain can have two repositories; the service chooses one database or both (CQRS). There is no mandatory command/query split — the choice is per module.

Connection pools

Both connections use an explicit pool (configured in database.config.ts). The GET /health endpoint includes pool metadata in the postgres and mongo checks.

EnvironmentPool
POSTGRES_POOL_MAX=10
POSTGRES_POOL_MIN=2
POSTGRES_POOL_IDLE=30000
POSTGRES_POOL_TIMEOUT=5000

MONGO_POOL_MAX=10
MONGO_POOL_MIN=0
MONGO_POOL_IDLE=30000
MONGO_POOL_TIMEOUT=5000

PostgreSQL

  • • Relational, typed schema (TypeORM)
  • • Repository: ResourcePostgresRepository
  • • Use as the single source of truth or as the query side in CQRS

MongoDB

  • • Flexible document schema (Mongoose)
  • • Repository: ResourceMongoRepository
  • • Use alone or as the command side in CQRS

Example: single database

TypeScriptResourceService
constructor(
  private readonly postgresRepo: ResourcePostgresRepository,
) {}

async create(data: CreateResourceDto) {
  return this.postgresRepo.create(data);
}

async findAll() {
  return this.postgresRepo.findAll();
}

Example: enable CQRS

Inject both repositories in the service: commands in one database, queries in the other. Optionally sync with Kafka for eventual consistency.

TypeScriptResourceService (CQRS)
constructor(
  private readonly mongoRepo: ResourceMongoRepository,      // commands
  private readonly postgresRepo: ResourcePostgresRepository, // queries
) {}

async create(data: CreateResourceDto) {
  const resource = await this.mongoRepo.create(data);
  // publish Kafka event to project into Postgres...
  return resource;
}

async findAll() {
  return this.postgresRepo.findAll();
}

Mongo or Postgres

You can use Postgres only, Mongo only, or both (CQRS). The choice is in the service/module without removing the other database infrastructure from the project.

How Technologies Relate

Relationship Diagram

NestJS

Framework Core

MongoDB

Available / CQRS

PostgreSQL

Persistence / queries

Redis

Cache

Kafka

Messaging

MinIO

Storage

Graylog

GELF Logs

  • NestJS ↔ MongoDB/PostgreSQL: The framework communicates with databases through Mongoose and TypeORM; the service chooses which repository to use
  • NestJS ↔ Redis: Cache interceptors use Redis to store results
  • NestJS ↔ Kafka: Events and messages are published/consumed via Kafka (useful for projecting data in CQRS)
  • NestJS ↔ MinIO: File upload and download via S3-compatible API
  • NestJS ↔ Graylog: Structured logs via GELF HTTP (interceptor + exception filter)
  • MongoDB ↔ PostgreSQL (optional): In a CQRS design, synchronization via Kafka events or async processes