PitchPro follows a classic two-tier architecture: a stateless REST API (the backend) and a Single Page Application (the frontend) that communicate exclusively over HTTP/JSON. The Express server at portDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/JuanSerna14/Final-lenguaje-Avanzado/llms.txt
Use this file to discover all available pages before exploring further.
8000 owns all business logic and data persistence, while the React SPA at port 5173 handles rendering and user interaction. Every request from the frontend carries a JWT Bearer token in the Authorization header; the backend’s verifyToken middleware validates it before any route handler executes. Neither tier shares code or process space — they are independently deployable projects.
Project structure
Database schema
The three tables are created at startup byinitDatabase() in arquimarket/src/db/init.ts. All timestamps use TIMESTAMPTZ (timezone-aware). The schema intentionally keeps the model lean — no migrations library is used; CREATE TABLE IF NOT EXISTS is idempotent.
| Constraint | Table | Rule |
|---|---|---|
REFERENCES canchas(id) | reservas.cancha_id | Foreign key — PostgreSQL rejects a booking that references a non-existent court. |
CONSTRAINT estado_valido | reservas.estado | Allowed values: pendiente, confirmada, cancelada. Any other string causes the INSERT/UPDATE to fail at the database level. |
CONSTRAINT origen_valido | reservas.origen | Allowed values: whatsapp, interfaz. Captures whether the booking was made via the web dashboard or an external channel. |
CONSTRAINT hora_valida | reservas.hora_fin | Enforces hora_fin > hora_inicio — it is impossible to persist a booking where the end time is before or equal to the start time. |
UNIQUE | users.email | One account per email address. The registration route also checks for duplicates at the application layer before the DB constraint fires. |
In addition to the database-level
CHECK constraints, all incoming request bodies are validated by Zod schemas (CrearCanchaSchema, CrearReservaSchema) in the controller layer before any SQL is executed. If Zod fails, a 422 (canchas) or 400 (reservas) is returned immediately, without touching the database.Request lifecycle
The following steps describe the full journey of an authenticated request — for example,GET /api/canchas from the React dashboard.
Axios interceptor injects the Bearer token
The frontend Axios instance reads This happens transparently — components call
sessionStorage.getItem('accessToken') and attaches it to every outgoing request as:canchasApi.list() with no knowledge of the header.CORS middleware validates the origin
Express runs
cors() as the first middleware. For local development, all origins are accepted. In production this should be restricted to the frontend’s domain.verifyToken middleware validates the JWT
verifyToken (mounted in main.ts before the route handler) reads the Authorization header, splits on ' ' to extract the raw token, and calls jwt.verify(token, JWT_SECRET). If the token is absent, malformed, or expired, the middleware immediately returns:401. The route handler never runs.On success, the decoded payload ({ id, email, iat, exp }) is attached to req.user for downstream use.Route handler executes business logic
Control passes to the matching controller function (e.g.
canchasRouter.get('/')). The controller may call a service method, which in turn delegates to a repository method that issues parameterised SQL against the PostgreSQL pool.PostgreSQL executes the query and returns rows
The
pg Pool maintains a connection pool to the database. The repository receives the result rows, the service applies any domain logic (e.g. throwing CanchaNoEncontradaError for a 404), and the controller serialises the response as JSON.Authentication tokens
PitchPro uses a dual-token JWT strategy: a short-lived access token for API authorization and a long-lived refresh token for silent renewal. Both are signed with HS256.| Token | Expiry | Secret env var | Payload | Storage |
|---|---|---|---|---|
| Access token | 15 minutes | JWT_SECRET | { id, email } | sessionStorage (key: accessToken) — cleared when the browser tab closes |
| Refresh token | 7 days | JWT_REFRESH_SECRET | { id } | PostgreSQL users.refresh_token column (server-side) and returned in the login response body |
- Login (
POST /api/auth/login) — Bcrypt verifies the password, then both tokens are generated. The refresh token is persisted tousers.refresh_tokenin the database and also returned in the response body so the client can store it. - API calls — The access token is sent as
Authorization: Bearer <token>on every protected request. - Refresh (
POST /api/auth/refresh) — The client sends the refresh token; the server verifies it against both the signature and the stored value in the DB, then issues a new access token. - Logout (
POST /api/auth/logout) — The server setsusers.refresh_token = NULL, invalidating the refresh token immediately. The client should also discard its stored tokens.
Module layout
The backend follows a three-layer controller → service → repository pattern for thecanchas and reservas modules:
- Controller handles only HTTP concerns (status codes, request parsing, Zod validation). It never touches
pgdirectly. - Service encapsulates rules like “a booking cannot reference a non-existent court” or throwing a typed
CanchaNoEncontradaErrorso the controller can map it to a404without checking raw DB results. - Repository issues raw SQL and returns typed TypeScript objects. Swapping the database engine only requires changing this layer.
reservas module omits a dedicated service file — overlap detection (existeSolapamiento) lives directly in the repository, and the controller imports canchasService to validate that the referenced court exists before creating a booking.
Backend Authentication
Deep dive into the JWT implementation, bcrypt rounds, and token rotation strategy.
API Overview
Full endpoint reference with request bodies, response shapes, and error codes.
Database Guide
PostgreSQL connection pool configuration, migration approach, and the seed script.
Canchas API
List, retrieve, and create sport courts — request/response examples and Zod schema.