323 lines
6.6 KiB
Markdown
323 lines
6.6 KiB
Markdown
# AGENTS.md - Coding Agent Guidelines
|
||
|
||
Bu dosya, bu repoda çalışan AI kodlama ajanları için rehberdir.
|
||
|
||
---
|
||
|
||
## 1. Build / Lint / Test Commands
|
||
|
||
```bash
|
||
# Development
|
||
npm run start:dev # Dev server with watch mode
|
||
npm run build # Production build (nest build)
|
||
|
||
# Linting & Formatting
|
||
npm run lint # ESLint with Prettier
|
||
npm run format # Prettier write
|
||
|
||
# Testing
|
||
npm run test # Run all unit tests
|
||
npm run test:watch # Watch mode
|
||
npm run test:e2e # End-to-end tests
|
||
npx jest src/path/to/file.spec.ts # Run single test file
|
||
npx jest --testNamePattern="test name" # Run specific test
|
||
|
||
# Database
|
||
npx prisma generate # Generate Prisma client (required after install)
|
||
npx prisma migrate dev # Run migrations
|
||
npx prisma db seed # Seed database
|
||
|
||
# Feeder Scripts
|
||
npm run feeder:historical # Historical data fetch
|
||
npm run feeder:live # Live match data fetch
|
||
npm run feeder:basketball # Basketball data fetch
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Code Style Guidelines
|
||
|
||
### Imports (Sıralama)
|
||
|
||
```typescript
|
||
// 1. NestJS/common imports
|
||
import { Controller, Get, Post, Body } from '@nestjs/common';
|
||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||
|
||
// 2. External packages
|
||
import { plainToInstance } from 'class-transformer';
|
||
import * as bcrypt from 'bcrypt';
|
||
|
||
// 3. Local imports (relative)
|
||
import { UsersService } from './users.service';
|
||
import { CreateUserDto } from './dto/user.dto';
|
||
import { ApiResponse, createSuccessResponse } from '../../common/types';
|
||
```
|
||
|
||
### Formatting
|
||
|
||
- **Single quotes** for strings
|
||
- **Trailing commas** always
|
||
- Prettier ile formatlama zorunlu
|
||
- Dosya sonu boş satır
|
||
|
||
### Types & Type Safety
|
||
|
||
- `strictNullChecks: true` - null/undefined kontrolü zorunlu
|
||
- `noImplicitAny: false` - any kullanımına izin var (Prisma dynamic access için)
|
||
- Fonksiyon return type belirt: `async findOne(id: string): Promise<User>`
|
||
- Interface > Type alias (objeler için)
|
||
|
||
### Naming Conventions
|
||
|
||
```typescript
|
||
// Classes & Interfaces: PascalCase
|
||
class UsersService {}
|
||
interface ApiResponse<T> {}
|
||
|
||
// Variables & Functions: camelCase
|
||
const userService = new UsersService();
|
||
async function findUserById() {}
|
||
|
||
// Constants: UPPER_SNAKE_CASE
|
||
const JWT_SECRET = 'secret';
|
||
const IS_PUBLIC_KEY = 'isPublic';
|
||
|
||
// Files: kebab-case
|
||
user.dto.ts;
|
||
users.service.ts;
|
||
predictions.processor.spec.ts;
|
||
|
||
// DTOs: Entity + Dto suffix
|
||
(CreateUserDto, UpdateUserDto, UserResponseDto);
|
||
```
|
||
|
||
---
|
||
|
||
## 3. DTO Pattern
|
||
|
||
### Request DTOs
|
||
|
||
```typescript
|
||
export class CreateUserDto {
|
||
@ApiPropertyOptional({ example: 'user@example.com' })
|
||
@IsEmail()
|
||
email: string;
|
||
|
||
@IsString()
|
||
@MinLength(8)
|
||
password: string;
|
||
|
||
@IsOptional()
|
||
@IsString()
|
||
firstName?: string;
|
||
}
|
||
```
|
||
|
||
### Response DTOs (Security Critical)
|
||
|
||
```typescript
|
||
@Exclude()
|
||
export class UserResponseDto {
|
||
@Expose()
|
||
id: string;
|
||
|
||
@Expose()
|
||
email: string;
|
||
|
||
// passwordHash intentionally NOT exposed
|
||
}
|
||
```
|
||
|
||
### Controller Usage
|
||
|
||
```typescript
|
||
@Get('me')
|
||
async getMe(@CurrentUser() user: User): Promise<ApiResponse<UserResponseDto>> {
|
||
const fullUser = await this.usersService.findOneWithDetails(user.id);
|
||
return createSuccessResponse(
|
||
plainToInstance(UserResponseDto, fullUser),
|
||
);
|
||
}
|
||
```
|
||
|
||
**KRITIK:** Asla raw Prisma entity döndürme. Her zaman Response DTO kullan.
|
||
|
||
---
|
||
|
||
## 4. Architecture Patterns
|
||
|
||
### Service Layer
|
||
|
||
```typescript
|
||
@Injectable()
|
||
export class UsersService extends BaseService<
|
||
User,
|
||
CreateUserDto,
|
||
UpdateUserDto
|
||
> {
|
||
constructor(prisma: PrismaService) {
|
||
super(prisma, 'User');
|
||
}
|
||
|
||
// Custom methods...
|
||
}
|
||
```
|
||
|
||
### Controller Layer
|
||
|
||
```typescript
|
||
@ApiTags('Users')
|
||
@ApiBearerAuth()
|
||
@Controller('users')
|
||
export class UsersController extends BaseController<
|
||
User,
|
||
CreateUserDto,
|
||
UpdateUserDto
|
||
> {
|
||
constructor(private readonly usersService: UsersService) {
|
||
super(usersService, 'User');
|
||
}
|
||
}
|
||
```
|
||
|
||
### API Response Format
|
||
|
||
```typescript
|
||
// All responses use this structure
|
||
{
|
||
"success": true,
|
||
"status": 200,
|
||
"message": "Success",
|
||
"data": { ... },
|
||
"errors": []
|
||
}
|
||
|
||
// Helper functions
|
||
createSuccessResponse(data, 'Message')
|
||
createErrorResponse('Message', 400, ['error1'])
|
||
createPaginatedResponse(items, total, page, limit)
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Error Handling
|
||
|
||
### Throw NestJS HTTP Exceptions
|
||
|
||
```typescript
|
||
// Correct
|
||
throw new NotFoundException('User not found');
|
||
throw new ConflictException('EMAIL_ALREADY_EXISTS');
|
||
throw new UnauthorizedException('INVALID_CREDENTIALS');
|
||
|
||
// Wrong
|
||
throw new Error('User not found'); // Don't use generic Error
|
||
```
|
||
|
||
### i18n Error Keys
|
||
|
||
```typescript
|
||
// Use translatable keys (check src/i18n/{lang}/errors.json)
|
||
throw new ConflictException('EMAIL_ALREADY_EXISTS');
|
||
// Translates to: "Email already exists" (en) / "Email zaten kayıtlı" (tr)
|
||
```
|
||
|
||
### Global Exception Filter
|
||
|
||
- Tüm hatalar HTTP 200 ile döner (status body içinde)
|
||
- `NODE_ENV=development` ise stack trace eklenir
|
||
- Validation hataları otomatik formatlanır
|
||
|
||
---
|
||
|
||
## 6. Testing
|
||
|
||
### Unit Test Structure
|
||
|
||
```typescript
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
|
||
describe('UsersService', () => {
|
||
let service: UsersService;
|
||
let prisma: PrismaService;
|
||
|
||
beforeEach(async () => {
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
providers: [
|
||
UsersService,
|
||
{ provide: PrismaService, useValue: mockPrisma },
|
||
],
|
||
}).compile();
|
||
|
||
service = module.get<UsersService>(UsersService);
|
||
});
|
||
|
||
it('should find user by id', async () => {
|
||
// Arrange
|
||
mockPrisma.user.findUnique.mockResolvedValue(mockUser);
|
||
|
||
// Act
|
||
const result = await service.findOne('id');
|
||
|
||
// Assert
|
||
expect(result).toEqual(mockUser);
|
||
});
|
||
});
|
||
```
|
||
|
||
### Mocking External Dependencies
|
||
|
||
```typescript
|
||
jest.mock('axios');
|
||
const mockedAxios = axios as jest.Mocked<typeof axios>;
|
||
|
||
beforeEach(() => {
|
||
jest.clearAllMocks();
|
||
mockedAxios.post.mockResolvedValue({ data: { ok: true } });
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Module Registration
|
||
|
||
Redis-enabled modüller için `app.module.ts`:
|
||
|
||
```typescript
|
||
const redisEnabled = process.env.REDIS_ENABLED === 'true';
|
||
|
||
@Module({
|
||
imports: [
|
||
...(redisEnabled ? [QueueModule, PredictionsModule] : []),
|
||
// ...
|
||
],
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Environment Variables
|
||
|
||
Zorunlu (`.env`):
|
||
|
||
```env
|
||
NODE_ENV=development
|
||
PORT=3005
|
||
DATABASE_URL=postgresql://postgres:password@localhost:15432/boilerplate_db
|
||
JWT_SECRET=your-secret-key
|
||
JWT_ACCESS_EXPIRATION=15m
|
||
REDIS_ENABLED=false
|
||
AI_ENGINE_URL=http://127.0.0.1:8000
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Pre-commit Checklist
|
||
|
||
1. `npm run lint` - Lint errors fixed
|
||
2. `npm run build` - Build succeeds
|
||
3. `npm run test` - All tests pass
|
||
4. Response DTOs used for all API responses
|
||
5. No secrets/credentials in code
|