back/ directory. It exposes a JSON REST API under the /api/v1 prefix and a Swagger UI at /api-docs.
Tech stack
| Technology | Version | Role |
|---|---|---|
| Node.js | 20 | Runtime |
| Express | 5 | HTTP framework |
| PostgreSQL | 16 | Database |
pg | — | Direct SQL queries |
| Knex.js | 3 | Migrations and seeders only |
| Argon2id | — | Password hashing |
| JSON Web Tokens | — | Authentication tokens |
| Winston | — | Structured logging |
| Swagger / swagger-autogen | — | API documentation |
Module structure
Thesrc/modules/ directory is organized by domain, not by layer. Every domain module contains five files with consistent naming.
Design patterns
Repository pattern
Every domain module contains a.repository.js file that is the only place in the codebase where SQL is written. Repositories accept a pg client, execute a query, and return rows. Services never touch the database directly.
This decouples business logic from data access: changing a query or adding a database index does not require touching service or controller code.
Service layer
Services contain domain business rules and are the only layer that enforces them. Examples:- Verifying that a PM is the
owner_idof a project before allowing archive or edit operations. - Checking that a user is active (
activo = true) before issuing a JWT. - Orchestrating multiple repository calls inside a single
pgtransaction.
MVC adapted
Controllers handle the HTTP cycle only. They parse the request, call the service, and format the response withsuccessResponse or errorResponse from utils/response.js. No conditional logic or business rules appear in controllers.
Middleware pipeline
All routes are mounted under/api/v1 in app.js. Protected routes pass through three middleware functions in order:
httpLogger is applied globally in app.js. authenticateToken and authorizeRole are applied per-router in each module’s .routes.js file, so public routes (e.g. POST /login, POST /register) skip authentication entirely.
httpLogger
Logging is implemented as a response-finish hook rather than request middleware, so the log line includes the final HTTP status code and elapsed milliseconds:info for 2xx/3xx, warn for 4xx, error for 5xx.
authenticateToken
Extracts the Bearer token from theAuthorization header, verifies it with jwt.verify(token, process.env.TOKEN), and attaches the decoded payload as req.usuario. Distinguishes between expired tokens (401) and invalid tokens (403).
authorizeRole
Receives an array of permitted roles and returns an async middleware. It queries the database for the user’s current role rather than trusting the role in the JWT payload, which means role changes take effect on the next request without requiring a new token.Database access: pg over ORM
Repositories use thepg connection pool directly — there is no ORM such as Sequelize or Prisma. SQL is written explicitly in each repository function. Knex.js is present in the project but is used only for migrations and seeders.
Knex.js configuration
knexfile.js configures the development connection from environment variables:
npx knex migrate:latest and seed data with npx knex seed:run from the back/ directory.
Winston logging
The Winston logger instance is created insrc/config/logger.js and imported by httpLogger. Log levels map directly to HTTP status ranges (see middleware pipeline above). All application log output goes through this single logger, making it straightforward to add transports (file, external service) in one place.
Swagger
Swagger documentation is auto-generated byswagger-autogen and written to src/config/swagger-output.json. The Swagger UI is served at:
package.json.
Related pages
Architecture overview
How the frontend and backend fit together, CORS setup, and the authentication flow.
Frontend architecture
Next.js App Router layout, Zustand state, services layer, and shadcn/ui components.