Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danielsl4/TFG_DAM_2526/llms.txt

Use this file to discover all available pages before exploring further.

FutsalManager uses PostgreSQL as its primary database and Redis for caching. This page covers how to initialize the schema, describes every table, and explains what is cached and for how long.

Initializing the database

Run the initialization script once against your PostgreSQL instance:
node init_db.js
The script creates all ENUM types and 14 tables using CREATE TABLE IF NOT EXISTS, so it is safe to re-run on an existing database. It reads the DATABASE_URL environment variable from .env or the process environment.
init_db.js does not run schema migrations. If the database schema changes after initial setup, apply changes separately before restarting the API.

ENUM types

The schema defines five custom PostgreSQL ENUM types used across multiple tables:
TypeValuesUsed in
match_phasefase_de_grupos, octavos, cuartos, semis, finalmatches.phase
match_statuspendiente, en_curso, finalizadomatches.status
event_typegol, tarjeta_amarilla, tarjeta_rojamatch_events.type
roleadmin, referee, userusers.role
vote_typelocal, empate, visitantematch_votes.vote

Tables

seasons

Represents a competition season. Only one season should have is_active = true at a time.
ColumnTypeDescription
idserial PK
namevarcharSeason name, e.g. "2025/2026"
start_datedateStart date
end_datedateEnd date
is_activebooleanWhether this is the current active season

groups

Competition groups within a season (e.g. Group A, Group B).
ColumnTypeDescription
idserial PK
namevarcharGroup name
season_idFK → seasonsOwning season (cascades on delete)

teams

Clubs participating in the league. Not tied to a specific season.
ColumnTypeDescription
idserial PK
namevarcharTeam name
kit_colorvarcharPrimary kit color
logo_urltextURL to team logo (Cloudinary)
delegatevarcharTeam delegate contact name
coachvarcharCoach name
phonevarcharContact phone number

players

Individual players, independent of any team or season.
ColumnTypeDescription
idserial PK
namevarcharFull name
birth_datedateDate of birth
photo_urltextURL to player photo (Cloudinary)

team_players

Links players to teams for a specific season, including their jersey number.
ColumnTypeDescription
idserial PK
team_idFK → teams
player_idFK → players
season_idFK → seasons
jersey_numbervarchar(10)Jersey number for this season
The combination of (team_id, player_id, season_id) is unique — a player can only appear once per team per season.

fields

Futsal pitches where matches are played.
ColumnTypeDescription
idserial PK
namevarcharField name
locationtextAddress or description

matches

Scheduled and completed matches between two teams.
ColumnTypeDescription
idserial PK
home_team_idFK → teamsHome team (nullable for placeholder entries)
away_team_idFK → teamsAway team (nullable for placeholder entries)
home_team_placeholdervarcharText label when team is not yet determined
away_team_placeholdervarcharText label when team is not yet determined
group_idFK → groups
season_idFK → seasons
field_idFK → fieldsPlaying venue (set null on delete)
datetimestampScheduled kick-off
home_goalsintegerFull-time goals, home team
away_goalsintegerFull-time goals, away team
home_penalty_goalsintegerPenalty shootout goals, home
away_penalty_goalsintegerPenalty shootout goals, away
phasematch_phaseCompetition phase
statusmatch_statusMatch lifecycle state
observationstextReferee notes
locked_byFK → usersUser who locked the match for editing
locked_attimestampWhen the lock was acquired

match_events

Individual events recorded during a match (goals, cards).
ColumnTypeDescription
idserial PK
match_idFK → matches
player_idFK → playersPlayer who triggered the event
typeevent_typeEvent type

users

Registered users of the platform.
ColumnTypeDescription
idserial PK
usernamevarcharUnique username
emailvarcharUnique email address
passwordvarcharBcrypt-hashed password
roleroleUser role (admin, referee, or user)
pointsintegerPrediction points accumulated
created_attimestampAccount creation time

team_followers

Many-to-many relationship between users and the teams they follow.
ColumnTypeDescription
user_idFK → users
team_idFK → teams
created_attimestampWhen the user started following
The primary key is (user_id, team_id).

team_stats

Aggregated standings for a team within a specific group and season. Updated as matches are recorded.
ColumnTypeDescription
idserial PK
team_idFK → teams
group_idFK → groups
season_idFK → seasons
playedintegerMatches played
wonintegerWins
drawnintegerDraws
lostintegerLosses
goals_forintegerGoals scored
goals_againstintegerGoals conceded
pointsintegerLeague points
yellow_cardsintegerTotal yellow cards
red_cardsintegerTotal red cards
The combination of (team_id, group_id, season_id) is unique.

player_stats

Aggregated statistics for a player within a season.
ColumnTypeDescription
idserial PK
player_idFK → players
season_idFK → seasons
goalsintegerGoals scored
yellow_cardsinteger
red_cardsinteger
matches_playedinteger
The combination of (player_id, season_id) is unique.

match_votes

Records user predictions for match outcomes. Each user can vote once per match.
ColumnTypeDescription
idserial PK
match_idFK → matches
user_idFK → users
votevote_typePredicted outcome
created_attimestamp
The combination of (match_id, user_id) is unique.
The match_votes table also has a points_awarded column (integer, default 0) added as a post-init migration. It is set to 1 when a match is finalized and the user’s prediction was correct.

audit_logs

Records admin and referee actions for auditing purposes.
ColumnTypeDescription
idserial PK
user_idFK → usersUser who performed the action (set null on delete)
actionvarcharAction identifier, e.g. "update_match"
entity_typevarcharType of entity affected, e.g. "match"
entity_idintegerID of the affected entity
detailsjsonbAdditional context as JSON
created_attimestampWhen the action occurred

Key relationships

  • team_players is the join table linking teams and players. Because it includes season_id, the same player can appear on different teams in different seasons.
  • team_stats tracks standings per (team_id, group_id, season_id) triple. This allows the same team to have separate standings rows if it participates in multiple groups or seasons.
  • player_stats aggregates per (player_id, season_id), so a player’s stats reset each season.
  • match_votes gives each user one prediction per match, with points accumulated in users.points.

Redis caching

The backend caches two categories of data in Redis to reduce database load:
DataCache key patternTTL
Individual match detailmatch:{id}30 seconds (CACHE_TTL_MS in cache.js)
Match lists (all, current, by season)all_matches, matches:current, matches:season:{id}30 seconds
Standingsstandings, standings:{season_id}1 hour
Global last admin activityglobal_last_activityNo TTL (updated on write)
All match-related cache keys are invalidated immediately whenever a match is updated or a new event is recorded. Standings are also flushed at that point. This means the TTL values are a safety net rather than the primary invalidation mechanism.

Build docs developers (and LLMs) love