🚀 Enterprise NestJS Boilerplate (Antigravity Edition)
FOR AI AGENTS & DEVELOPERS: This documentation is structured to provide deep context, architectural decisions, and operational details to ensure seamless handover to any AI coding assistant (like Antigravity) or human developer.
🧠 Project Context & Architecture (Read Me First)
This is an opinionated, production-ready backend boilerplate built with NestJS. It is designed to be scalable, type-safe, and fully localized.
🏗️ Core Philosophy
- Type Safety First: Strict TypeScript configuration.
anyis forbidden. DTOs are the source of truth. - Generic Abstraction:
BaseServiceandBaseControllerhandle 80% of CRUD operations, allowing developers to focus on business logic. - i18nNative: Localization is not an afterthought. It is baked into the exception filters, response interceptors, and guards.
- Security by Default: JWT Auth, RBAC (Role-Based Access Control), Throttling, and Helmet are pre-configured.
📐 Architectural Decision Records (ADR)
To understand WHY things are the way they are:
-
Handling i18n Assets:
- Problem: Translation JSON files are not TypeScript code, so
tscignores them during build. - Solution: We configured
nest-cli.jsonwith"assets": ["i18n/**/*"]. This ensuressrc/i18nis copied todist/i18nautomatically. - Note: When running with
node, ensuredist/main.jscan find these files.
- Problem: Translation JSON files are not TypeScript code, so
-
Global Response Wrapping:
- Mechanism:
ResponseInterceptorwraps all successful responses. - Feature: It automatically translates the "Operation successful" message based on the
Accept-Languageheader usingI18nService. - Output Format:
{ "success": true, "status": 200, "message": "İşlem başarıyla tamamlandı", // Translated "data": { ... } }
- Mechanism:
-
Centralized Error Handling:
- Mechanism:
GlobalExceptionFiltercatches allHttpExceptionand unknownErrortypes. - Feature: It accepts error keys (e.g.,
AUTH_REQUIRED) and translates them usingi18n. If a translation is found inerrors.json, it is returned; otherwise, the original message is shown.
- Mechanism:
-
UUID Generation:
- Decision: We use Node.js native
crypto.randomUUID()instead of the externaluuidpackage to avoid CommonJS/ESM compatibility issues.
- Decision: We use Node.js native
🚀 Quick Start for AI & Humans
1. Prerequisites
- Node.js: v20.19+ (LTS)
- Docker: For running PostgreSQL and Redis effortlessly.
- Package Manager:
npm(Lockfile:package-lock.json)
2. Environment Setup
cp .env.example .env
# ⚠️ CRITICAL: Ensure DATABASE_URL includes the username!
# Example: postgresql://postgres:password@localhost:5432/boilerplate_db
3. Installation & Database
# Install dependencies
npm ci
# Start Infrastructure (Postgres + Redis)
docker-compose up -d postgres redis
# Generate Prisma Client (REQUIRED after install)
npx prisma generate
# Run Migrations
npx prisma migrate dev
# Seed Database (Optional - Creates Admin & Roles)
npx prisma db seed
4. Running the App
# Debug Mode (Watch) - Best for Development
npm run start:dev
# Production Build & Run
npm run build
npm run start:prod
🛡️ Response Standardization & Type Safety Protocol
This boilerplate enforces a strict "No-Leak" policy for API responses to ensure both Security and Developer Experience.
1. The unknown Type is Forbidden
- Rule: Controllers must NEVER return
ApiResponse<unknown>or raw Prisma entities. - Why: Returning raw entities risks exposing sensitive fields like
passwordhashes or internal metadata. It also breaks contract visibility for frontend developers.
2. DTO Pattern & Serialization
- Tool: We use
class-transformerfor all response serialization. - Implementation:
- All Response DTOs must use
@Exclude()class-level decorator. - Only fields explicitly marked with
@Expose()are returned to the client. - Controllers use
plainToInstance(UserResponseDto, data)before returning data.
- All Response DTOs must use
Example:
// ✅ Good: Secure & Typed
@Get('me')
async getMe(@CurrentUser() user: User): Promise<ApiResponse<UserResponseDto>> {
return createSuccessResponse(plainToInstance(UserResponseDto, user));
}
// ❌ Bad: Leaks password hash & Weak Types
@Get('me')
async getMe(@CurrentUser() user: User) {
return createSuccessResponse(user);
}
⚡ High-Performance Caching (Redis Strategy)
To ensure enterprise-grade performance, we utilize Redis for caching frequently accessed data (e.g., Roles, Permissions).
- Library:
@nestjs/cache-managerwithcache-manager-redis-yet(Supports Redis v6+ / v7). - Configuration: Global Cache Module in
AppModule. - Strategy: Read-heavy endpoints use
@UseInterceptors(CacheInterceptor). - Invalidation: Write operations (Create/Update/Delete) manually invalidate relevant cache keys.
Usage:
// 1. Automatic Caching
@Get('roles')
@UseInterceptors(CacheInterceptor)
@CacheKey('roles_list') // Unique Key
@CacheTTL(60000) // 60 Seconds
async getAllRoles() { ... }
// 2. Manual Invalidation (Inject CACHE_MANAGER)
async createRole(...) {
// ... create role logic
await this.cacheManager.del('roles_list'); // Clear cache
}
🤖 Gemini AI Integration (Optional)
This boilerplate includes an optional AI module powered by Google's Gemini API. It's disabled by default and can be enabled during CLI setup or manually.
Configuration
Add these to your .env file:
# Enable Gemini AI features
ENABLE_GEMINI=true
# Your Google API Key (get from https://aistudio.google.com/apikey)
GOOGLE_API_KEY=your-api-key-here
# Model to use (optional, defaults to gemini-2.5-flash)
GEMINI_MODEL=gemini-2.5-flash
Usage
The GeminiService is globally available when enabled:
import { GeminiService } from './modules/gemini';
@Injectable()
export class MyService {
constructor(private readonly gemini: GeminiService) {}
async generateContent() {
// Check if Gemini is available
if (!this.gemini.isAvailable()) {
throw new Error('AI features are not enabled');
}
// 1. Simple Text Generation
const { text, usage } = await this.gemini.generateText(
'Write a product description for a coffee mug',
);
// 2. With System Prompt & Options
const { text } = await this.gemini.generateText('Translate: Hello World', {
systemPrompt: 'You are a professional Turkish translator',
temperature: 0.3,
maxTokens: 500,
});
// 3. Multi-turn Chat
const { text } = await this.gemini.chat([
{ role: 'user', content: 'What is TypeScript?' },
{
role: 'model',
content: 'TypeScript is a typed superset of JavaScript...',
},
{ role: 'user', content: 'Give me an example' },
]);
// 4. Structured JSON Output
interface ProductData {
name: string;
price: number;
features: string[];
}
const { data } = await this.gemini.generateJSON<ProductData>(
'Generate a product entry for a wireless mouse',
'{ name: string, price: number, features: string[] }',
);
console.log(data.name, data.price); // Fully typed!
}
}
Available Methods
| Method | Description |
|---|---|
isAvailable() |
Check if Gemini is properly configured and ready |
generateText(prompt, options?) |
Generate text from a single prompt |
chat(messages, options?) |
Multi-turn conversation |
generateJSON<T>(prompt, schema, options?) |
Generate and parse structured JSON |
Options
interface GeminiGenerateOptions {
model?: string; // Override default model
systemPrompt?: string; // System instructions
temperature?: number; // Creativity (0-1)
maxTokens?: number; // Max response length
}
🌍 Internationalization (i18n) Guide
Unique to this project is the deep integration of nestjs-i18n.
- Location:
src/i18n/{lang}/ - Files:
common.json: Generic messages (success, welcome)errors.json: Error codes (AUTH_REQUIRED, USER_NOT_FOUND)validation.json: Validation messages (IS_EMAIL)auth.json: Auth specific success messages (LOGIN_SUCCESS)
How to Translate a New Error:
- Throw an exception with a key:
throw new ConflictException('EMAIL_EXISTS'); - Add
"EMAIL_EXISTS": "Email already taken"tosrc/i18n/en/errors.json. - Add Turkish translation to
src/i18n/tr/errors.json. - Start server; the
GlobalExceptionFilterhandles the rest.
🧪 Testing & CI/CD
- GitHub Actions:
.github/workflows/ci.ymlhandles build and linting checks on push. - Local Testing:
npm run test # Unit tests npm run test:e2e # End-to-End tests
📂 System Map (Directory Structure)
src/
├── app.module.ts # Root module (Redis, Config, i18n setup)
├── main.ts # Entry point
├── common/ # Shared resources
│ ├── base/ # Abstract BaseService & BaseController (CRUD)
│ ├── types/ # Interfaces (ApiResponse, PaginatedData)
│ ├── filters/ # Global Exception Filter
│ └── interceptors/ # Response Interceptor
├── config/ # Application configuration
├── database/ # Prisma Service
├── i18n/ # Localization assets
└── modules/ # Feature modules
├── admin/ # Admin capabilities (Roles, Permissions + Caching)
│ ├── admin.controller.ts
│ └── dto/ # Admin Response DTOs
├── auth/ # Authentication layer
├── gemini/ # 🤖 Optional AI module (Google Gemini)
├── health/ # Health checks
└── users/ # User management
🛠️ Troubleshooting (Known Issues)
1. EADDRINUSE: address already in use
- Fix:
lsof -ti:3000 | xargs kill -9
2. PrismaClientInitializationError / Database Connection Hangs
- Fix: Check
.envDATABASE_URL. Ensuredocker-compose upis running.
3. Cache Manager Deprecation Warnings
- Context:
cache-manager-redis-yetmay show deprecation warnings regardingKeyv. This is expected as we wait for the ecosystem to stabilize oncache-managerv6/v7. The current implementation is fully functional.
📃 License
This project is proprietary and confidential.