Blackterz is a Node.js + Express REST API that follows a strict three-layer MVC pattern. Every HTTP request flows through a predictable chain — from route registration to controller logic to a SQL-only model — with a Vanilla JS frontend served as static files from the same Express process. Understanding how each layer is defined, and what it is explicitly forbidden from doing, is the fastest way to navigate the codebase.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Blackterz2/Proyecto_5to_Semestre/llms.txt
Use this file to discover all available pages before exploring further.
Project File Structure
The Three MVC Layers
The architecture enforces a single responsibility per layer. No layer is permitted to perform the work of another.Routes
Map a URL + HTTP verb to exactly one controller function. Contain zero logic — just
router.get('/:id', verificarToken, getById).Controllers
Parse and validate
req, call one or more model functions, shape the JSON response with the correct HTTP status code.Models
Execute parameterized SQL against the connection pool. Transform tabular
rows[] results into nested JavaScript objects. Know nothing about HTTP.The model layer is the only place that imports
pool from src/config/db.js. If you see a SQL string anywhere outside src/models/, that is a bug.Request Lifecycle
Every incoming request traverses this chain in order. Middlewares execute before any route handler is reached.Database Connection Pool
src/config/db.js creates a single shared mysql2/promise pool that every model imports.
Why a pool instead of a single connection?
Why a pool instead of a single connection?
A single
mysql.createConnection() can only process one query at a time. With 20 simultaneous requests, 19 of them queue up behind the first.A pool keeps up to connectionLimit: 10 connections open simultaneously. Incoming requests are distributed across available connections automatically. When all 10 are busy, waitForConnections: true holds the request in queue (queueLimit: 0 means the queue is unbounded) rather than throwing an error. If a connection drops, the pool discards it and creates a fresh one.The mysql2/promise variant returns Promises natively from pool.execute() and pool.query(), enabling clean async/await syntax throughout the model layer without wrappers.pool.execute() for parameterized queries:
pool.getConnection() to acquire a dedicated connection, issue BEGIN / COMMIT / ROLLBACK, and release it in the finally block.
Static Frontend Serving
public/ directory relative to src/server.js. The custom setHeaders callback adds the correct MIME type for .avif exercise images, which Node.js does not recognize by default.
Because the frontend is served from the same origin as the API, all fetch() calls in app.js use relative paths:
index.html
Served automatically at
GET / — Express looks for index.html in the static root by convention.avatars/
Files uploaded with multer land in
public/images/avatars/. They are immediately available at /images/avatars/avatar-{id}.jpg without any extra route.Test Entry Point Guard
require.main === module is true only when the file is executed directly (node src/server.js or npm start). When a test file imports the app with require('../src/server'), the app.listen() call is skipped entirely. The test suite uses supertest, which simulates HTTP requests against the Express app object without binding any real port. This means tests run in any environment, including CI, without port conflicts.
