Populate
Cómo el parámetro de consulta populate incrusta relaciones, la lista blanca exacta por endpoint, los valores por defecto de cada endpoint y las reglas para las rutas anidadas.
Las relaciones son opcionales. Por defecto cada endpoint devuelve sus propias
columnas más un conjunto pequeño de relaciones; todo lo demás lo pides con
populate, una lista separada por comas de rutas permitidas. Una clave de
relación está ausente del JSON por completo si no se populó — así que
'divisions' in tournament es una prueba confiable de "¿pedí esto?", y una ruta
desconocida es un 400 en lugar de un parámetro ignorado en silencio.
Sintaxis
GET /v1/external/tournaments/{idx}?populate=club,divisions,divisions.rounds- Separado por comas, sin espacios.
- El orden no importa.
- Repetir un valor es inofensivo.
- Un valor fuera de la lista blanca del endpoint es un
400— consulta Errores.
populate reemplaza el valor por defecto, no lo extiende
En cuanto envías un parámetro populate, la lista por defecto del endpoint
desaparece. GET /v1/external/tournaments/{idx} devuelve club y divisions
cuando no mandas nada; ?populate=divisions devuelve las categorías y ningún
club. Si quieres ambos, pide ambos.
Las rutas anidadas necesitan a sus padres
Las rutas usan puntos: draws.rounds.games.score.sets. No tienes que listar a
los ancestros — la API los expande por ti, así que
populate=draws.rounds.games.score.sets implica draws, draws.rounds,
draws.rounds.games y draws.rounds.games.score. Listarlos explícitamente no
cambia nada.
Lo inverso no es cierto: popular un padre no trae a sus hijos.
populate=games te da partidos a los que les faltan las claves homeTeam y
score; para esas necesitas games.homeTeam y games.score.
Listas blancas por endpoint
GET /v1/external/tournaments
Las filas de listado se mantienen deliberadamente someras — este es el único endpoint que pagina, así que una fila gorda se multiplica por 100.
| Permitido | Por defecto |
|---|---|
club | (ninguno) |
GET /v1/external/tournaments/{idx}
| Permitido | Por defecto |
|---|---|
club, club.courts, divisions, divisions.rounds, sponsors, circuit, circuitCategory, rankedTiebreakRules | club,divisions |
GET /v1/external/tournaments/{idx}/divisions
| Permitido | Por defecto |
|---|---|
rounds, teams, teams.playerProfiles, teams.leagueTeam, circuitCategory | rounds |
GET /v1/external/tournaments/{idx}/teams
| Permitido | Por defecto |
|---|---|
playerProfiles, captainProfile, leagueTeam, division | playerProfiles,division |
GET /v1/external/rounds/{idx}
El endpoint más rico, y aquel cuyo valor por defecto conviene saberse de memoria.
Permitido:
division
pools
pools.teams
pools.standings
pools.standings.team
pools.standings.teamStats
games
games.homeTeam
games.awayTeam
games.winner
games.court
games.score
games.score.sets
draws
draws.rounds
draws.rounds.games
draws.rounds.games.homeTeam
draws.rounds.games.awayTeam
draws.rounds.games.winner
draws.rounds.games.court
draws.rounds.games.score
draws.rounds.games.score.setsPor defecto, cuando no mandas populate:
division
pools
pools.teams
games
games.homeTeam
games.awayTeam
games.winner
draws.rounds
draws.rounds.games
draws.rounds.games.homeTeam
draws.rounds.games.awayTeam
draws.rounds.games.winner
draws.rounds.games.score
draws.rounds.games.score.setsEl valor por defecto trae los marcadores del cuadro, pero no los de la lista plana
Fíjate en la asimetría: el valor por defecto popula
draws.rounds.games.score (+ sets) pero no games.score. Si estás
leyendo resultados del arreglo plano games, pide
games.score,games.score.sets explícitamente — y recuerda que hacerlo
reemplaza toda la lista por defecto, así que vuelve a listar todo lo demás que
necesites.
Las claves de populate no siempre son claves de la respuesta
Tres rutas se incrustan bajo un nombre distinto al que pides:
| Lo que populas | Cómo aparece |
|---|---|
playerProfiles (o teams.playerProfiles) | players: [{ idx, firstName, lastName, avatar }] |
captainProfile | captainProfileIdx: string | null |
pools.standings.teamStats | standings[].teamStats |
Ejemplos resueltos
Un cuadro público para una ronda de eliminación — llaves, ganadores y marcadores, sin tablas de grupo:
curl -s -G https://api.setto.io/v1/external/rounds/2e7f5b10-8c94-4d63-a1b8-7f0e3d6c2a95 \
-H "Authorization: Bearer $SETTO_API_TOKEN" \
-d populate=draws.rounds.games.homeTeam,draws.rounds.games.awayTeam,draws.rounds.games.winner,draws.rounds.games.score.setsUna tabla de clasificación de fase de grupos:
curl -s -G https://api.setto.io/v1/external/rounds/9d3c6a84-7e15-4b02-8f6d-1c4a9e7b53f2 \
-H "Authorization: Bearer $SETTO_API_TOKEN" \
-d populate=pools.standings.team,pools.standings.teamStatsUn listado completo de categorías con cada pareja inscrita y sus jugadores:
curl -s -G https://api.setto.io/v1/external/tournaments/3c9b7f52-6d41-4a8e-b1f0-2e7d5c9a4b18/divisions \
-H "Authorization: Bearer $SETTO_API_TOKEN" \
-d populate=teams,teams.playerProfilesCosto
Cada ruta populada son más joins y más filas. Tres hábitos mantienen las respuestas rápidas:
- Pide las hojas que renderizas, nada más.
pools.standings.teamStatssinpools.standings.teames una petición legal y más barata si ya conoces los equipos. - No popules en el endpoint de listado a menos que necesites el club. Toma los ids del listado y luego trae el único torneo que vas a renderizar.
- Cachea. Los payloads de una ronda cambian solo cuando se captura un resultado. Combina un TTL corto con peticiones condicionales y la página de una ronda te cuesta un puñado de peticiones por hora en lugar de una por visitante.
Modelo de datos
El árbol de dominio de SETTO — organización, torneo, categoría, ronda, grupo, cuadro y partido — más las convenciones de identificadores y los valores de enum sobre los que vas a ramificar.
Paginación
Cómo funcionan limit y offset en GET /v1/external/tournaments, qué contiene el sobre meta, y por qué los demás endpoints de listado devuelven todo de una vez.