Tournament List — Add TournamentSummary Schema
Overview
The dashboard's tournament list endpoints (GET /tournaments/ and GET /tournaments/me/) currently return the full TournamentRead schema, which includes volunteer_schema, time_blocks, and categories — none of which are needed to render a tournament card. This issue introduces a lightweight TournamentSummary schema for use on list endpoints, replacing the heavy full schema with only the fields the card view actually consumes.
Motivation
TournamentRead includes VolunteerSchema (containing custom_fields and positions), time_blocks, and categories. For a dashboard card that shows only name, dates, location, event count, and volunteer count, this is significant unnecessary payload. As tournament data grows, this overhead will compound across every dashboard load. A dedicated summary schema keeps list responses fast and the API contract explicit about what each endpoint is meant to support.
Backend Changes
1. Add TournamentSummary schema
File: backend/app/schemas/tournament.py
class TournamentSummary(BaseModel):
id: int
name: str
start_date: datetime | None
end_date: datetime | None
location: str | None
event_count: int
volunteer_count: int
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
2. Compute counts in the list routes
event_count and volunteer_count are not columns — they must be derived from relationships. Use the existing .events and .memberships relationships already loaded on the Tournament model:
TournamentSummary(
**tournament.__dict__,
event_count=len(tournament.events),
volunteer_count=len(tournament.memberships),
)
If relationship loading becomes a performance concern at scale, this can be revisited with SQL subqueries — but the relationship approach is the correct starting point given the current data volumes.
3. Update list routes
File: backend/app/api/routes/tournaments.py
GET /tournaments/ → response_model=list[TournamentSummary]
GET /tournaments/me/ → response_model=list[TournamentSummary]
The detail route GET /tournaments/{tournament_id}/ continues to return the full TournamentRead — the tournament settings and events pages need everything.
Frontend Changes
Wherever the tournament list response is consumed (dashboard page, tournament card component), update the TypeScript type to match the new summary shape:
- Remove:
volunteer_schema, time_blocks, categories
- Add:
event_count: number, volunteer_count: number
The dashboard card should surface event_count and volunteer_count as secondary metadata (e.g. "12 events · 47 volunteers").
Tests
File: backend/tests/api/test_tournaments.py
Update list endpoint tests to assert against the TournamentSummary shape:
volunteer_schema, time_blocks, and categories should not be present in list responses
event_count and volunteer_count should be present and correct
- The detail endpoint (
GET /tournaments/{id}/) should still return the full schema
Relevant Files
| File |
Change |
backend/app/schemas/tournament.py |
Add TournamentSummary |
backend/app/api/routes/tournaments.py |
Update response_model on list routes |
backend/app/models/models.py |
Reference only — .events and .memberships relationships used for counts |
backend/tests/api/test_tournaments.py |
Update list response assertions |
frontend/ (dashboard page + tournament card component) |
Update TypeScript type for list responses |
Out of Scope
- Pagination on list endpoints — a separate concern; this issue only changes the response shape
- SQL subquery optimization for count fields — can be done later if relationship loading proves slow
- Detail route —
GET /tournaments/{id}/ continues to return TournamentRead unchanged
Tournament List — Add
TournamentSummarySchemaOverview
The dashboard's tournament list endpoints (
GET /tournaments/andGET /tournaments/me/) currently return the fullTournamentReadschema, which includesvolunteer_schema,time_blocks, andcategories— none of which are needed to render a tournament card. This issue introduces a lightweightTournamentSummaryschema for use on list endpoints, replacing the heavy full schema with only the fields the card view actually consumes.Motivation
TournamentReadincludesVolunteerSchema(containingcustom_fieldsandpositions),time_blocks, andcategories. For a dashboard card that shows only name, dates, location, event count, and volunteer count, this is significant unnecessary payload. As tournament data grows, this overhead will compound across every dashboard load. A dedicated summary schema keeps list responses fast and the API contract explicit about what each endpoint is meant to support.Backend Changes
1. Add
TournamentSummaryschemaFile:
backend/app/schemas/tournament.py2. Compute counts in the list routes
event_countandvolunteer_countare not columns — they must be derived from relationships. Use the existing.eventsand.membershipsrelationships already loaded on theTournamentmodel:If relationship loading becomes a performance concern at scale, this can be revisited with SQL subqueries — but the relationship approach is the correct starting point given the current data volumes.
3. Update list routes
File:
backend/app/api/routes/tournaments.pyGET /tournaments/→response_model=list[TournamentSummary]GET /tournaments/me/→response_model=list[TournamentSummary]The detail route
GET /tournaments/{tournament_id}/continues to return the fullTournamentRead— the tournament settings and events pages need everything.Frontend Changes
Wherever the tournament list response is consumed (dashboard page, tournament card component), update the TypeScript type to match the new summary shape:
volunteer_schema,time_blocks,categoriesevent_count: number,volunteer_count: numberThe dashboard card should surface
event_countandvolunteer_countas secondary metadata (e.g. "12 events · 47 volunteers").Tests
File:
backend/tests/api/test_tournaments.pyUpdate list endpoint tests to assert against the
TournamentSummaryshape:volunteer_schema,time_blocks, andcategoriesshould not be present in list responsesevent_countandvolunteer_countshould be present and correctGET /tournaments/{id}/) should still return the full schemaRelevant Files
backend/app/schemas/tournament.pyTournamentSummarybackend/app/api/routes/tournaments.pyresponse_modelon list routesbackend/app/models/models.py.eventsand.membershipsrelationships used for countsbackend/tests/api/test_tournaments.pyfrontend/(dashboard page + tournament card component)Out of Scope
GET /tournaments/{id}/continues to returnTournamentReadunchanged