Skip to content

Programming rules (Backend)

Juan Manuel Gallo Lòpez edited this page Oct 1, 2026 · 1 revision

Backend Programming and Architecture Rules

These rules are mandatory for all backend code created, modified, or refactored in the project.

The backend is developed using NestJS and TypeScript.


1. General Principles

  • TypeScript: Avoid using any unless there is a justified reason.
  • Single Responsibility: Each module, Controller, Service, Entity, DTO, Guard, or helper must contain only the logic that belongs to its responsibility.
  • Reusability: Do not duplicate logic. Shared validations or behaviors must be centralized in the appropriate Service, Guard, helper, or reusable function.
  • Consistency: Follow the same naming, structure, and organization conventions across similar files.
  • Imports: Keep imports organized and remove unused imports.

2. Module Structure

Each main functionality must have its own NestJS module.

Examples:

users
restaurants
reservations
reviews

AppModule is responsible only for connecting and configuring the main application modules.

It must not contain business logic.

A typical feature should follow this structure:

restaurant.module.ts
restaurant.controller.ts
restaurant.service.ts
restaurant.entity.ts

dto/
  create-restaurant.dto.ts
  update-restaurant.dto.ts

3. Backend Architecture and Responsibilities

The backend must follow this communication flow:

HTTP Request
     |
     v
Controller
     |
     v
Service
     |
     v
Repository
     |
     v
Database

Each layer has a specific responsibility.

Controllers

Controllers are responsible for:

  • Receiving HTTP requests.
  • Reading route parameters, query parameters, or request body data.
  • Calling the corresponding Service.
  • Returning the response.

Controllers must not:

  • Access the database directly.
  • Access TypeORM repositories directly.
  • Contain business logic.

Services

Services contain:

  • Business logic.
  • Application operations.
  • Database operations through TypeORM repositories.

Only Services may access TypeORM repositories directly.

Repositories

Repositories are responsible for communicating with the database through TypeORM.

They are accessed through Services, not directly from Controllers.


4. Entities and Database Relationships

Entities represent database tables.

They must contain only:

  • Column definitions.
  • Primary and foreign keys.
  • Relationships between Entities.

Example relationships include:

OneToMany
ManyToOne
OneToOne

For Entity relationships, use Relation<T> when appropriate to avoid circular reference issues.

Example:

Relation<Restaurant>

Entities must not be used directly as the request body structure received from the frontend.


5. DTOs

Data received from the frontend must be handled through DTOs.

Examples:

create-restaurant.dto.ts
update-restaurant.dto.ts

DTOs define the data required for a specific operation.

For example:

POST /api/restaurants
-> CreateRestaurantDto

PATCH /api/restaurants/:id
-> UpdateRestaurantDto

Do not use Entities directly as request DTOs.


6. API Routes and HTTP Methods

API routes must represent resources, not actions.

Correct:

GET    /api/restaurants
GET    /api/restaurants/:id
POST   /api/restaurants
PATCH  /api/restaurants/:id
DELETE /api/restaurants/:id

Avoid:

/getRestaurants
/createRestaurant
/deleteRestaurant

Use HTTP methods according to their purpose:

GET
-> Retrieve information.

POST
-> Create information.

PATCH
-> Partially update information.

PUT
-> Completely replace information.

DELETE
-> Delete information.

7. Asynchronous Database Operations

Operations that access the database must be asynchronous.

Use:

async
await

Example:

async findAll() {
  return await this.restaurantRepository.find();
}

8. Error Handling

Use the HTTP exceptions provided by NestJS to report application errors.

Examples:

NotFoundException
BadRequestException
UnauthorizedException

Use the exception that correctly represents the error being returned.

Example:

if (!restaurant) {
  throw new NotFoundException('Restaurant not found');
}

9. Application States

Repeated application states must be defined using shared enum or type definitions instead of manually writing the same values in multiple files.

Example:

export enum ReservationStatus {
  PENDING = 'pending',
  CONFIRMED = 'confirmed',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled',
}

This prevents inconsistencies between different parts of the application.


10. Authentication and Authorization

Authentication and authorization must be handled using NestJS Guards and decorators.

Controllers must not manually repeat user or role validations.

For example, authentication or authorization logic should be handled through mechanisms such as:

AuthGuard
RoleGuard
Decorators

This keeps access-control logic separate from Controller responsibilities.


11. Environment Configuration

Configuration values that are sensitive or depend on the execution environment must be stored in environment variables.

Examples:

Application port
Database URL
Authentication secrets
Frontend URL

These values must not be hardcoded directly into the application source code.


12. CORS

CORS must be configured globally in:

main.ts

This configuration must allow the frontend to communicate with the backend according to the project's environment settings.


13. Database Migrations

Changes to the database structure must be handled through migrations.

Migrations must remain in the project history so that database changes can be tracked and reproduced.

Examples of structural changes include:

Creating a table
Adding a column
Removing a column
Changing a relationship
Adding a constraint

14. Complete Application Flow

Communication between the frontend and backend follows this structure:

Vue
 |
 v
Frontend Service
 |
 v
Axios
 |
 v
REST API
 |
 v
Controller
 |
 v
Service
 |
 v
TypeORM Repository
 |
 v
Database

Each layer must perform only the responsibility assigned to it.