main
This commit is contained in:
335
README.md
Normal file
335
README.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# 🚀 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.
|
||||
Reference in New Issue
Block a user