336 lines
11 KiB
Markdown
336 lines
11 KiB
Markdown
# 🚀 Enterprise NestJS Boilerplate (Antigravity Edition)
|
||
|
||
[](https://nestjs.com/)
|
||
[](https://www.typescriptlang.org/)
|
||
[](https://www.prisma.io/)
|
||
[](https://www.postgresql.org/)
|
||
[](https://www.docker.com/)
|
||
|
||
> **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. `any` is forbidden. DTOs are the source of truth.
|
||
- **Generic Abstraction:** `BaseService` and `BaseController` handle 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:_
|
||
|
||
1. **Handling i18n Assets:**
|
||
- **Problem:** Translation JSON files are not TypeScript code, so `tsc` ignores them during build.
|
||
- **Solution:** We configured `nest-cli.json` with `"assets": ["i18n/**/*"]`. This ensures `src/i18n` is copied to `dist/i18n` automatically.
|
||
- **Note:** When running with `node`, ensure `dist/main.js` can find these files.
|
||
|
||
2. **Global Response Wrapping:**
|
||
- **Mechanism:** `ResponseInterceptor` wraps all successful responses.
|
||
- **Feature:** It automatically translates the "Operation successful" message based on the `Accept-Language` header using `I18nService`.
|
||
- **Output Format:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"status": 200,
|
||
"message": "İşlem başarıyla tamamlandı", // Translated
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
3. **Centralized Error Handling:**
|
||
- **Mechanism:** `GlobalExceptionFilter` catches all `HttpException` and unknown `Error` types.
|
||
- **Feature:** It accepts error keys (e.g., `AUTH_REQUIRED`) and translates them using `i18n`. If a translation is found in `errors.json`, it is returned; otherwise, the original message is shown.
|
||
|
||
4. **UUID Generation:**
|
||
- **Decision:** We use Node.js native `crypto.randomUUID()` instead of the external `uuid` package to avoid CommonJS/ESM compatibility issues.
|
||
|
||
---
|
||
|
||
## 🚀 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
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# ⚠️ CRITICAL: Ensure DATABASE_URL includes the username!
|
||
# Example: postgresql://postgres:password@localhost:5432/boilerplate_db
|
||
```
|
||
|
||
### 3. Installation & Database
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
# 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 `password` hashes or internal metadata. It also breaks contract visibility for frontend developers.
|
||
|
||
### 2. DTO Pattern & Serialization
|
||
|
||
- **Tool:** We use `class-transformer` for 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.
|
||
|
||
**Example:**
|
||
|
||
```typescript
|
||
// ✅ 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-manager` with `cache-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:**
|
||
|
||
```typescript
|
||
// 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:
|
||
|
||
```env
|
||
# 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:
|
||
|
||
```typescript
|
||
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
|
||
|
||
```typescript
|
||
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:**
|
||
|
||
1. Throw an exception with a key: `throw new ConflictException('EMAIL_EXISTS');`
|
||
2. Add `"EMAIL_EXISTS": "Email already taken"` to `src/i18n/en/errors.json`.
|
||
3. Add Turkish translation to `src/i18n/tr/errors.json`.
|
||
4. Start server; the `GlobalExceptionFilter` handles the rest.
|
||
|
||
---
|
||
|
||
## 🧪 Testing & CI/CD
|
||
|
||
- **GitHub Actions:** `.github/workflows/ci.yml` handles build and linting checks on push.
|
||
- **Local Testing:**
|
||
```bash
|
||
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 `.env` `DATABASE_URL`. Ensure `docker-compose up` is running.
|
||
|
||
**3. Cache Manager Deprecation Warnings**
|
||
|
||
- **Context:** `cache-manager-redis-yet` may show deprecation warnings regarding `Keyv`. This is expected as we wait for the ecosystem to stabilize on `cache-manager` v6/v7. The current implementation is fully functional.
|
||
|
||
---
|
||
|
||
## 📃 License
|
||
|
||
This project is proprietary and confidential.
|