Repository navigation
Programming rules (Backend)
These rules are mandatory for all backend code created, modified, or refactored in the project.
The backend is developed using NestJS and TypeScript.
-
TypeScript: Avoid using
anyunless 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.
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
The backend must follow this communication flow:
HTTP Request
|
v
Controller
|
v
Service
|
v
Repository
|
v
Database
Each layer has a specific responsibility.
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 contain:
- Business logic.
- Application operations.
- Database operations through TypeORM repositories.
Only Services may access TypeORM repositories directly.
Repositories are responsible for communicating with the database through TypeORM.
They are accessed through Services, not directly from Controllers.
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.
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.
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.
Operations that access the database must be asynchronous.
Use:
async
awaitExample:
async findAll() {
return await this.restaurantRepository.find();
}Use the HTTP exceptions provided by NestJS to report application errors.
Examples:
NotFoundException
BadRequestException
UnauthorizedExceptionUse the exception that correctly represents the error being returned.
Example:
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}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.
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.
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.
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.
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
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.