114 lines
4.5 KiB
Markdown
114 lines
4.5 KiB
Markdown
# Coding Style
|
|
|
|
Conventions for writing and reviewing code in Negareh API. These align with the project's Cursor rules and existing patterns.
|
|
|
|
## TypeScript
|
|
|
|
- **Strict typing** — Never use `any`. Prefer `unknown` when the type is genuinely unknown, then narrow.
|
|
- **Exported functions** — Add explicit return types.
|
|
- **Interfaces for DTOs** — Use classes with decorators for request bodies; interfaces for internal shapes and transformers.
|
|
- **Enums** — Use only when a fixed set of values is shared across layers (e.g. `PermissionEnum`, status enums). Avoid enums for one-off string unions.
|
|
- **Readonly** — Prefer `readonly` on constructor-injected dependencies and immutable fields.
|
|
- **Function size** — Keep functions under ~40 lines when practical; extract private methods for clarity.
|
|
|
|
## NestJS patterns
|
|
|
|
### Dependency injection
|
|
|
|
- Use **constructor injection** for all dependencies.
|
|
- Register providers in the feature module; export only what other modules need.
|
|
- Use `forwardRef()` when circular module dependencies are unavoidable (e.g. `AuthModule` ↔ `UserModule`).
|
|
|
|
### Controllers
|
|
|
|
- Keep controllers **thin** — validate input, call service, return result.
|
|
- Use `@ApiTags`, `@ApiOperation`, and `@ApiBearerAuth` for Swagger.
|
|
- Apply guards at method level: `@UseGuards(AuthGuard)` or `@UseGuards(AdminAuthGuard)`.
|
|
- Extract identity from decorators: `@UserId()`, `@AdminId()`.
|
|
|
|
### Services
|
|
|
|
- All **business logic** belongs in services (`providers/`).
|
|
- Throw NestJS `HttpException` subclasses (`BadRequestException`, `NotFoundException`, etc.).
|
|
- Use `ConfigService` instead of `process.env`.
|
|
|
|
### DTOs
|
|
|
|
Every request body and query object must have a DTO class:
|
|
|
|
```typescript
|
|
import { IsNotEmpty, IsString } from 'class-validator';
|
|
import { ApiProperty } from '@nestjs/swagger';
|
|
import { Transform } from 'class-transformer';
|
|
|
|
export class ExampleDto {
|
|
@IsNotEmpty()
|
|
@IsString()
|
|
@ApiProperty({ example: 'value' })
|
|
@Transform(({ value }) => value?.trim())
|
|
name: string;
|
|
}
|
|
```
|
|
|
|
- Use `class-validator` decorators for validation.
|
|
- Use `class-transformer` (`@Transform`) for normalization (e.g. phone numbers).
|
|
- Document fields with `@ApiProperty` for Swagger.
|
|
|
|
## File and naming conventions
|
|
|
|
| Artifact | Convention | Example |
|
|
|----------|------------|---------|
|
|
| Module | `<feature>.module.ts` | `order.module.ts` |
|
|
| Controller | `<feature>.controller.ts` in `controllers/` | `order.controller.ts` |
|
|
| Service | `<feature>.service.ts` in `providers/` | `order.service.ts` |
|
|
| Repository | `<entity>.repository.ts` in `repositories/` | `order.repository.ts` |
|
|
| Entity | `<entity>.entity.ts` in `entities/` | `order.entity.ts` |
|
|
| DTO | `<action>-<entity>.dto.ts` in `dto/` | `create-order.dto.ts` |
|
|
| Event | `<Entity><Action>Event` in `events/` | `OrderCreatedEvent` |
|
|
| Listener | `<feature>.listeners.ts` in `listeners/` | `order.listeners.ts` |
|
|
|
|
- Use **kebab-case** for file names.
|
|
- Use **PascalCase** for classes, **camelCase** for methods and variables.
|
|
- Folder names: `controllers`, `providers`, `repositories`, `entities`, `dto`, `listeners`, `events`, `enum`.
|
|
|
|
## MikroORM usage
|
|
|
|
- Prefer **EntityRepository** subclasses over raw `EntityManager` queries in services.
|
|
- Use **`populate`** instead of manual joins.
|
|
- Use **`wrap(entity).assign(dto)`** for partial updates.
|
|
- Use **`em.transactional()`** when updating multiple entities atomically.
|
|
- Use **`Collection`** for `OneToMany` relations.
|
|
- Avoid **N+1** — batch loads and selective `populate`.
|
|
- Avoid **raw SQL** unless necessary (triggers, complex reports). Migrations are the exception.
|
|
- Type repository injections explicitly.
|
|
|
|
## Performance
|
|
|
|
- Minimize database round-trips; batch where possible.
|
|
- Paginate large lists — return `PaginatedResult<T>` with `data` and `meta`.
|
|
- Select only required columns; avoid loading full entity graphs when a subset suffices.
|
|
- Index foreign keys and frequently filtered columns.
|
|
|
|
## Error handling
|
|
|
|
- Use specific HTTP exceptions with clear messages.
|
|
- Shared user-facing messages live in `src/common/enums/message.enum.ts`.
|
|
- Do not swallow errors silently; log unexpected failures with NestJS `Logger`.
|
|
|
|
## Formatting and linting
|
|
|
|
```bash
|
|
npm run format # Prettier
|
|
npm run lint # ESLint (with fix)
|
|
npm run build # TypeScript compile check
|
|
```
|
|
|
|
Match the style of surrounding code. Do not reformat unrelated files in the same change.
|
|
|
|
## Before adding new code
|
|
|
|
1. Search for an existing implementation in `src/modules/` and `src/common/`.
|
|
2. Reuse existing services, DTOs, and repositories.
|
|
3. Follow the module's existing folder layout.
|
|
4. Extend rather than duplicate.
|