Request Processing Pipeline¶
Relevant source files * index.js * src/middlewares/verifyToken.js * src/router.js
Purpose and Scope¶
This document describes how incoming requests flow through the application's processing pipeline, from initial receipt to final response. It covers the complete middleware stack for HTTP requests, WebSocket connection establishment, authentication/authorization layers, and response generation mechanisms.
For information about specific route definitions and handlers, see Routing System. For authentication implementation details, see Authentication & Authorization. For WebSocket event handling after connection is established, see Real-time Communication System.
Overview of Request Flow¶
The application handles two distinct types of incoming connections: HTTP/HTTPS requests and WebSocket connections. Both share some common processing steps but diverge in their pipeline structure.
flowchart TD
Incoming["Incoming Connection"]
ProtocolCheck["Protocol Type"]
StaticCheck["Static Asset?"]
WSAuth["Socket.IO Authentication"]
ServeStatic["Direct File Serve"]
MiddlewareChain["Middleware Chain"]
MW1["helmet"]
MW2["express-rate-limit"]
MW3["cookie-parser"]
MW4["express.urlencoded + express.json"]
MW5["express-session"]
MW6["i18n.init"]
MW7["setGlobals"]
RouterLayer["Router Layer"]
RouteMatch["Route Found?"]
NotFound["404 Response"]
AuthCheck["Auth Required?"]
Handler["Route Handler"]
TokenMW["verifyToken Middleware"]
AdminMW["verifyAdmin Middleware"]
Unauthorized["401 Unauthorized"]
Forbidden["403 Forbidden"]
ResponseType["Response Type"]
EJSRender["EJS Template Render"]
JSONResponse["JSON Response"]
PDFGen["PDF Generation"]
HTTPRedirect["HTTP Redirect"]
WSConnect["Establish Connection"]
WSReject["Connection Rejected"]
WSRooms["Assign to Rooms"]
End["Send Response"]
WSEventLoop["Event Loop Active"]
Incoming --> ProtocolCheck
ProtocolCheck --> StaticCheck
ProtocolCheck --> WSAuth
StaticCheck --> ServeStatic
StaticCheck --> MiddlewareChain
MiddlewareChain --> MW1
MW1 --> MW2
MW2 --> MW3
MW3 --> MW4
MW4 --> MW5
MW5 --> MW6
MW6 --> MW7
MW7 --> RouterLayer
RouterLayer --> RouteMatch
RouteMatch --> NotFound
RouteMatch --> AuthCheck
AuthCheck --> Handler
AuthCheck --> TokenMW
AuthCheck --> AdminMW
TokenMW --> Handler
TokenMW --> Unauthorized
AdminMW --> Handler
AdminMW --> Forbidden
Handler --> ResponseType
ResponseType --> EJSRender
ResponseType --> JSONResponse
ResponseType --> PDFGen
ResponseType --> HTTPRedirect
WSAuth --> WSConnect
WSAuth --> WSReject
WSConnect --> WSRooms
ServeStatic --> End
EJSRender --> End
JSONResponse --> End
PDFGen --> End
HTTPRedirect --> End
NotFound --> End
Unauthorized --> End
Forbidden --> End
WSReject --> End
WSRooms --> WSEventLoop
Sources: index.js L1-L86
HTTP Middleware Stack¶
The HTTP request pipeline processes requests through a series of middleware functions before reaching route handlers. Each middleware layer is registered in the application bootstrap file and executes in a specific order.
Middleware Execution Order¶
| Order | Middleware | Purpose | Configuration Location |
|---|---|---|---|
| 1 | express.urlencoded |
Parse URL-encoded request bodies | index.js L41 |
| 2 | express.json |
Parse JSON request bodies | index.js L42 |
| 3 | Static file server | Serve files from /public directory |
index.js L43 |
| 4 | swaggerUi.serve |
Serve Swagger documentation UI | index.js L44 |
| 5 | securityMiddleware |
Helmet + rate limiting + security headers | index.js L45 |
| 6 | i18n.init |
Initialize internationalization | index.js L46 |
| 7 | setGlobals |
Set global template variables | index.js L47 |
| 8 | Router | Match routes and dispatch to handlers | index.js L48 |
Sources: index.js L36-L48
Body Parsing Layer¶
The first processing stage handles request body parsing. Two parsers are configured:
flowchart TD
Request["HTTP Request"]
URLCheck["Content-Type?"]
URLParser["express.urlencoded<br>extended: true"]
JSONParser["express.json"]
Continue["Continue to next middleware"]
PopulateBody["req.body populated<br>with form data"]
NextMiddleware["Next Middleware"]
Request --> URLCheck
URLCheck --> URLParser
URLCheck --> JSONParser
URLCheck --> Continue
URLParser --> PopulateBody
JSONParser --> PopulateBody
PopulateBody --> NextMiddleware
Continue --> NextMiddleware
The extended: true option in express.urlencoded allows parsing of rich objects and arrays encoded in URL format. This is used by form submissions throughout the application, including the login form at src/router.js L532-L601
and registration form at src/router.js L414-L484
Sources: index.js L41-L42
Cookie Parsing Layer¶
Cookie parsing is initialized before the middleware chain to make cookies available to all subsequent middleware:
- Middleware:
cookie-parserregistered at index.js L37 - Purpose: Parses the
Cookieheader and populatesreq.cookiesobject - Critical for: JWT token retrieval (
req.cookies.token), language preference (req.cookies.lang) - Used by:
verifyTokenmiddleware at src/middlewares/verifyToken.js L4 homepage route at src/router.js L61 language system
Sources: index.js L37
src/middlewares/verifyToken.js L1-L18
Security Middleware Layer¶
The security middleware is implemented in a separate module and includes multiple protection mechanisms:
flowchart TD
IncomingReq["HTTP Request"]
SecurityMW["securityMiddleware"]
Helmet["helmet<br>Security Headers"]
RateLimit["express-rate-limit<br>Global Rate Limiter"]
CSRFCheck["CSRF Protection<br>(if configured)"]
InputVal["Input Validation<br>express-validator"]
SecurityOK["Security Checks Passed"]
BlockedHelmet["Insecure Request"]
BlockedRate["429 Too Many Requests"]
BlockedCSRF["403 Invalid CSRF Token"]
BlockedVal["400 Validation Error"]
NextMW["Continue to i18n"]
IncomingReq --> SecurityMW
SecurityMW --> Helmet
Helmet --> RateLimit
RateLimit --> CSRFCheck
CSRFCheck --> InputVal
InputVal --> SecurityOK
Helmet --> BlockedHelmet
RateLimit --> BlockedRate
CSRFCheck --> BlockedCSRF
InputVal --> BlockedVal
SecurityOK --> NextMW
The module is loaded at index.js L15
and registered at index.js L45
Additional route-specific rate limiting is applied to the authentication endpoint using the limiter middleware at src/router.js L21
and applied at src/router.js L532
Sources: index.js L15
Internationalization Layer¶
The i18n middleware handles language detection and translation:
flowchart TD
Request["HTTP Request"]
I18nInit["i18n.init"]
CheckCookie["Check 'lang' cookie"]
UseCookie["Set locale from cookie"]
CheckQuery["Check ?lang= param"]
UseQuery["Set locale from query"]
UseDefault["Use defaultLocale: 'es'"]
SetLocale["Set req.locale"]
MakeHelpers["Create req.__()<br>and req.__n() helpers"]
NextMW["Continue to setGlobals"]
Request --> I18nInit
I18nInit --> CheckCookie
CheckCookie --> UseCookie
CheckCookie --> CheckQuery
CheckQuery --> UseQuery
CheckQuery --> UseDefault
UseCookie --> SetLocale
UseQuery --> SetLocale
UseDefault --> SetLocale
SetLocale --> MakeHelpers
MakeHelpers --> NextMW
Configuration is at index.js L23-L31
with these settings:
- Locales:
en,es - Directory:
locales/(JSON files) - Default: Spanish (
es) - Cookie name:
lang - Query parameter:
lang
Language switching is handled by the /set-lang/:lang route at src/router.js L398-L407
which sets the language cookie and redirects back.
Sources: index.js L23-L31
Global Variables Middleware¶
The setGlobals middleware injects global variables into all view templates:
- Location: index.js L18 (import), index.js L47 (registration)
- Purpose: Make common data available to all EJS templates without passing explicitly
- Typical variables: Current user, application settings, i18n helpers, environment flags
Sources: index.js L18
Route Matching and Authentication¶
After passing through the middleware chain, requests reach the router layer where routes are matched and authentication is enforced.
Route Registration Pattern¶
Routes are defined in the router module and exported for use by the application:
flowchart TD
RouterModule["src/router.js"]
DefineRoutes["Define Route Handlers"]
PublicRoutes["Public Routes<br>router.get('/')<br>router.get('/login')<br>router.get('/registro')"]
UserRoutes["User Routes<br>verifyToken required"]
AdminRoutes["Admin Routes<br>verifyToken + verifyAdmin"]
NoAuth["No middleware<br>Direct handler"]
VerifyTokenMW["verifyToken<br>middleware"]
VerifyAdminMW["verifyAdmin<br>middleware<br>(includes verifyToken)"]
Handler["Route Handler<br>Function"]
Export["module.exports = router"]
IndexJS["index.js"]
Mount["app.use('/', router)"]
RouterModule --> DefineRoutes
DefineRoutes --> PublicRoutes
DefineRoutes --> UserRoutes
DefineRoutes --> AdminRoutes
PublicRoutes --> NoAuth
UserRoutes --> VerifyTokenMW
AdminRoutes --> VerifyAdminMW
NoAuth --> Handler
VerifyTokenMW --> Handler
VerifyAdminMW --> Handler
RouterModule --> Export
Export --> IndexJS
IndexJS --> Mount
Sources: src/router.js L1-L608
Authentication Middleware Hierarchy¶
The application implements a two-tier authentication system:
Tier 1: verifyToken¶
Located at src/middlewares/verifyToken.js L1-L18
this middleware:
- Extracts JWT from
req.cookies.tokenat src/middlewares/verifyToken.js L4 - Returns
401 No autenticadoif token is missing at src/middlewares/verifyToken.js L6 - Verifies token signature using
jwt.verify()at src/middlewares/verifyToken.js L9 - Decodes payload and attaches to
req.userat src/middlewares/verifyToken.js L10 - Returns
403 Token inválidoif verification fails at src/middlewares/verifyToken.js L13 - Calls
next()to continue to route handler at src/middlewares/verifyToken.js L11
Payload structure in req.user:
{
user: string, // Username (from usuarios.usuario)
name: string, // Display name (from usuarios.nombre)
rol: string, // Role: 'admin' or 'user'
imagen: string // Profile image filename
}
Tier 2: verifyAdmin¶
Located at src/middlewares/verifyAdmin.js
this middleware:
- Assumes
verifyTokenhas already run and populatedreq.user - Checks if
req.user.rol === 'admin' - Returns
403 Acceso denegadoif user is not an admin - Calls
next()if user is admin
Sources: src/middlewares/verifyToken.js L1-L18
Route Authentication Examples¶
| Route | Path | Middleware | File Location |
|---|---|---|---|
| Homepage | GET / |
None | src/router.js L59-L74 |
| Login page | GET /login |
None | src/router.js L75-L77 |
| Admin panel | GET /admin |
verifyToken |
src/router.js L119-L134 |
| Support chat | GET /soporte |
verifyToken |
src/router.js L220-L227 |
| Get messages (admin) | GET /api/mensajes |
verifyAdmin |
src/router.js L229-L253 |
| Get user list | GET /api/usuarios-conversaciones |
verifyAdmin |
src/router.js L283-L315 |
| PDF download | GET /pdf/descargar |
verifyToken |
src/router.js L317-L353 |
| Login POST | POST /auth |
limiter (rate limit) |
src/router.js L532-L601 |
Sources: src/router.js L59-L601
Request Handler Dispatch¶
Once a route is matched and authentication passes, the request is dispatched to the appropriate handler. Handlers can be inline functions or imported controllers.
Handler Types by Response Format¶
flowchart TD
Handler["Route Handler Invoked"]
CheckType["Response Type"]
ResRender["res.render()"]
ResJSON["res.json()"]
ResRedirect["res.redirect()"]
ResPDF["res.send(pdfBuffer)"]
ResDownload["res.download()"]
EJSEngine["EJS Template Engine"]
CompileEJS["Compile Template"]
InjectData["Inject res.render() data"]
ApplyLayout["Apply layout.ejs"]
SendHTML["Send HTML Response"]
SerializeJSON["Serialize to JSON"]
SetContentType["Content-Type:<br>application/json"]
SendJSON["Send JSON Response"]
SetLocation["Set Location header"]
Send302["Send 302 Response"]
SetPDFHeaders["Content-Type: application/pdf<br>Content-Disposition: attachment"]
SendPDF["Send PDF Buffer"]
Handler --> CheckType
CheckType --> ResRender
CheckType --> ResJSON
CheckType --> ResRedirect
CheckType --> ResPDF
CheckType --> ResDownload
ResRender --> EJSEngine
EJSEngine --> CompileEJS
CompileEJS --> InjectData
InjectData --> ApplyLayout
ApplyLayout --> SendHTML
ResJSON --> SerializeJSON
SerializeJSON --> SetContentType
SetContentType --> SendJSON
ResRedirect --> SetLocation
SetLocation --> Send302
ResPDF --> SetPDFHeaders
SetPDFHeaders --> SendPDF
View Rendering Handlers¶
Example at src/router.js L119-L134
for the /admin route:
router.get("/admin", verifyToken, (req, res) => {
db.query("SELECT * FROM productos", (error, results) => {
if (error) {
throw error;
} else {
res.render("admin", {
productos: results,
user: req.user,
login: true,
rol: req.user.rol,
});
}
});
});
The res.render() call:
- Loads the
views/admin.ejstemplate - Passes data object as template variables
- Compiles EJS with data
- Sends HTML response
JSON API Handlers¶
Example at src/router.js L256-L280
for /api/mensajes/mios:
router.get("/api/mensajes/mios", verifyToken, (req, res) => {
const usuario = req.user.user;
// ... SQL query ...
db.query(sql, [usuario, usuario], (err, results) => {
if (err) {
return res.status(500).json({ error: "Error interno" });
}
res.json(results);
});
});
The res.json() method automatically:
- Serializes JavaScript object/array to JSON string
- Sets
Content-Type: application/jsonheader - Sends response
Redirect Handlers¶
Example at src/router.js L215-L218
for /logout:
router.get("/logout", (req, res) => {
res.clearCookie("token");
res.redirect('/');
});
PDF Generation Handlers¶
Two approaches are used:
- Puppeteer (HTML-to-PDF) at src/router.js L317-L353 : * Renders EJS template to HTML string * Launches headless Chrome browser * Converts HTML to PDF buffer * Sends buffer with appropriate headers
- PDFKit (Programmatic) at src/router.js L355-L396 : * Creates
PDFDocumentinstance * Programmatically writes text and graphics * Pipes directly to response stream * More control, less convenient
Sources: src/router.js L119-L396
Controller Module Integration¶
Some routes delegate to controller functions imported from ./controllers:
const crud = require("./controllers");
// ...
router.post("/save", crud.save);
router.post("/update", crud.update);
This pattern separates route definition from business logic. Located at src/router.js L13
(import) and src/router.js L603-L604
(usage).
Sources: src/router.js L13
WebSocket Connection Pipeline¶
WebSocket connections follow a different pipeline than HTTP requests, but share the authentication mechanism.
Connection Establishment Flow¶
sequenceDiagram
participant Socket.IO Client
participant HTTP Server
participant Socket.IO Server
participant Socket.IO Auth Middleware
participant setupSocket Handler
participant MySQL Database
Socket.IO Client->>HTTP Server: WebSocket Upgrade Request
HTTP Server->>Socket.IO Server: (includes Cookie header)
Socket.IO Server->>Socket.IO Auth Middleware: Hand off to Socket.IO
Socket.IO Auth Middleware->>Socket.IO Auth Middleware: Run authentication middleware
Socket.IO Auth Middleware->>Socket.IO Auth Middleware: Extract JWT from cookie
loop [User is
Socket.IO Auth Middleware->>Socket.IO Server: jwt.verify(token)
Socket.IO Server->>setupSocket Handler: Authentication Success
setupSocket Handler->>setupSocket Handler: Attach user to socket
setupSocket Handler->>setupSocket Handler: Trigger 'connection' event
setupSocket Handler->>Socket.IO Client: socket.join('user:username')
Socket.IO Client->>setupSocket Handler: socket.join('admins')
Socket.IO Auth Middleware->>Socket.IO Client: Connection Established
end
Socket.IO Server Initialization¶
The Socket.IO server is created and attached to the HTTP server in index.js L8-L12
:
const http = require("http");
const socketIO = require("socket.io");
const server = http.createServer(app);
const io = socketIO(server);
The socket handler is configured at index.js L14
(import) and index.js L57
(setup):
const setupSocket = require("./src/sockets/socketHandler");
// ...
setupSocket(io);
Sources: index.js L8-L14
WebSocket Authentication Middleware¶
The WebSocket authentication middleware is part of the setupSocket function in src/sockets/socketHandler.js. It:
- Runs before the
connectionevent fires - Extracts JWT from the socket handshake cookies
- Verifies the token using the same
JWT_SECRET - Attaches user information to
socket.user - Rejects connection if token is invalid
This ensures that only authenticated users can establish WebSocket connections and that socket.user is available in all event handlers.
Sources: index.js L14
Response Generation Pipeline¶
The final stage of request processing is generating and sending the response.
Response Type Decision Tree¶
flowchart TD
HandlerComplete["Handler Logic Complete"]
DataReady["Data Ready"]
ResponseMethod["res method called"]
StartEJS["EJS Rendering"]
StartJSON["JSON Serialization"]
StartRedirect["HTTP Redirect"]
StartSend["Direct Buffer Send"]
StartStatus["Status + Message"]
LoadTemplate["Load EJS Template File"]
CompileTemplate["Compile with Data"]
ApplyLayoutEJS["Apply Layout Template"]
SetHTMLHeaders["Content-Type: text/html"]
SendHTMLResponse["Send HTML to Client"]
SerializeToJSON["JSON.stringify()"]
SetJSONHeaders["Content-Type: application/json"]
SendJSONResponse["Send JSON to Client"]
SetLocationHeader["Location: redirect_path"]
Set302Status["Status: 302 Found"]
SendRedirectResponse["Send Redirect to Client"]
SetContentTypeHeader["Set Content-Type header"]
SendBufferResponse["Send Buffer to Client"]
SetStatusCode["Set HTTP Status Code"]
SendStatusResponse["Send Message to Client"]
CloseConnection["Close Connection"]
HandlerComplete --> DataReady
DataReady --> ResponseMethod
ResponseMethod --> StartEJS
ResponseMethod --> StartJSON
ResponseMethod --> StartRedirect
ResponseMethod --> StartSend
ResponseMethod --> StartStatus
StartEJS --> LoadTemplate
LoadTemplate --> CompileTemplate
CompileTemplate --> ApplyLayoutEJS
ApplyLayoutEJS --> SetHTMLHeaders
SetHTMLHeaders --> SendHTMLResponse
StartJSON --> SerializeToJSON
SerializeToJSON --> SetJSONHeaders
SetJSONHeaders --> SendJSONResponse
StartRedirect --> SetLocationHeader
SetLocationHeader --> Set302Status
Set302Status --> SendRedirectResponse
StartSend --> SetContentTypeHeader
SetContentTypeHeader --> SendBufferResponse
StartStatus --> SetStatusCode
SetStatusCode --> SendStatusResponse
SendHTMLResponse --> CloseConnection
SendJSONResponse --> CloseConnection
SendRedirectResponse --> CloseConnection
SendBufferResponse --> CloseConnection
SendStatusResponse --> CloseConnection
EJS Template Rendering Pipeline¶
The EJS view engine is configured at index.js L52
:
app.set("view engine", "ejs");
When a handler calls res.render("admin", { data }):
- Express resolves
"admin"toviews/admin.ejs - EJS compiles the template with the provided data object
- Template can access data as local variables
- Template typically extends
layout.ejsfor consistent structure - Partials are included using
<%- include('partials/header') %> - Final HTML is sent with
Content-Type: text/html; charset=utf-8
Example usage at src/router.js L126-L131
:
res.render("admin", {
productos: results,
user: req.user,
login: true,
rol: req.user.rol,
});
Sources: index.js L52
Error Response Handling¶
Error responses are handled at various levels:
| Error Type | HTTP Status | Response Method | Example Location |
|---|---|---|---|
| Not authenticated | 401 | res.status(401).send() |
src/middlewares/verifyToken.js L6 |
| Invalid token | 403 | res.status(403).send() |
src/middlewares/verifyToken.js L13 |
| Access denied (not admin) | 403 | res.status(403).json() |
Admin middleware |
| Missing parameters | 400 | res.status(400).json() |
src/router.js L233 |
| Database error | 500 | res.status(500).json() |
src/router.js L247 |
| Route not found | 404 | Express default handler | Built-in |
Sources: src/middlewares/verifyToken.js L6-L13
Special Case: Rate-Limited Authentication¶
The authentication POST route includes an additional rate limiter middleware specific to login attempts:
const limiter = require("./middlewares/authLimiter");
// ...
router.post("/auth", limiter, async (req, res) => {
// ... authentication logic ...
});
Located at src/router.js L21
(import) and src/router.js L532
(application).
This provides brute-force protection by limiting the number of authentication attempts from a single IP address. The limiter runs before the authentication logic, so invalid attempts consume the rate limit budget.
Sources: src/router.js L21
Request Pipeline Summary¶
Complete HTTP Request Lifecycle¶
- Connection received by Node.js HTTP server
- Static asset check - served directly if matched
- Body parsing - URL-encoded and JSON parsers
- Cookie parsing - extract cookies into
req.cookies - Security layer - helmet, rate limiting, CSRF protection
- Internationalization - detect and set language
- Global variables - inject common template data
- Route matching - find handler for request path
- Authentication (if required) - verify JWT token
- Authorization (if required) - check user role
- Handler execution - business logic runs
- Response generation - render view, serialize JSON, or generate PDF
- Send response - transmit to client and close connection
Complete WebSocket Connection Lifecycle¶
- Upgrade request received with cookies
- Socket.IO handshake - negotiate connection parameters
- Authentication middleware - verify JWT from cookie
- Connection established -
connectionevent fires - Room assignment - join personal room and admin room (if applicable)
- Event loop active - listen for
mensaje_privadoand other events - Disconnect handling - cleanup on connection close
Sources: index.js L1-L86