Files
2026-07-02 15:59:46 +03:30

4.5 KiB

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. AuthModuleUserModule).

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:

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

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.