Covers architecture, setup, database schema, API reference, and image pipeline; link from README. Co-authored-by: Cursor <cursoragent@cursor.com>
4.0 KiB
Art Gallery — REST API
Base URL in development:
- Direct:
http://localhost:3001 - Via Vite proxy:
http://localhost:5173(same paths)
All JSON responses use Content-Type: application/json. Errors return { "error": "message" } with an appropriate HTTP status.
Static images are served at /images/<relative-path> from IMAGE_DIR.
GET /api/bounds
Returns the overall timeline year range used to initialise the zoomable timeline.
Response
{
"min_year": -800,
"max_year": 2100
}
min_year is the earliest art movement start; max_year is the latest of era ends, movement ends, and artist death years.
GET /api/timeline
Historical eras and art movements overlapping a year window.
Query
| Param | Type | Default | Description |
|---|---|---|---|
start |
int | -3000 | Window start year |
end |
int | 2100 | Window end year |
Response
{
"eras": [ { "id": 1, "name": "Renaissance", "start_year": 1300, "end_year": 1600, "start_definite": false, "end_definite": false, "description": "...", "sort_order": 3 } ],
"movements": [ { "id": 5, "name": "Impressionism", "start_year": 1860, "end_year": 1890, "era_id": 6, "era_name": "Modern", "color": "#87CEEB", ... } ]
}
Movements are filtered to those with at least one artist active in the requested range.
GET /api/artists
Artists for timeline portraits and movement bands.
Query
| Param | Type | Description |
|---|---|---|
start |
int | Only artists alive after this year |
end |
int | Only artists born before this year |
movement_id |
int | Filter by movement |
Response — array of artist objects with joined movement_name and movement_color.
GET /api/movements/:id/artists
Artists belonging to a single movement.
Response — array of { id, name, birth_year, death_year, portrait_path, bio_short }.
GET /api/artists/:id
Full artist profile for the bio page and 3D gallery entry.
Response
{
"artist": { "id": 1, "name": "...", "movement_name": "...", "bio_full": "...", ... },
"periods": [ { "id": 1, "name": "Milan Period", "start_year": 1482, "end_year": 1499, ... } ],
"paintings": [ { "id": 10, "title": "...", "year": 1498, "image_path": "...", "thumbnail_path": "...", ... } ]
}
POST /api/artists/:id/preload-images
Fast local scan: links paintings to files already on disk. Does not download from the internet (safe to call before opening the 3D gallery).
Response
{ "fetched": 12, "total": 15 }
fetched counts paintings with a resolvable local image after the scan.
GET /api/paintings/:id
Painting detail with influence graph neighbours.
Response
{
"painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", ... },
"influencedBy": [ { "id": 3, "title": "...", "artist_name": "...", "notes": "...", "aspects": "...", "quote": "...", "source_author": "...", ... } ],
"influenced": [ { "id": 20, "title": "...", ... } ]
}
GET /api/paintings/:id/image
Serves a painting image file. Resolves and caches from external sources only when no local file exists.
Query
| Param | Values | Default |
|---|---|---|
size |
thumb, full |
thumb |
Returns the image bytes with Cache-Control: public, max-age=86400, or 404 if unavailable.
Frontend helpers
The React client wraps these endpoints in client/src/api/client.ts:
| Function | Maps to |
|---|---|
api.getBounds() |
GET /api/bounds |
api.getTimeline(start, end) |
GET /api/timeline |
api.getArtists(...) |
GET /api/artists |
api.getArtist(id) |
GET /api/artists/:id |
api.getPainting(id) |
GET /api/paintings/:id |
preloadArtistImages(id) |
POST /api/artists/:id/preload-images |
imageUrl(path) |
/images/<path> or placeholder |
galleryImageUrl(painting) |
Local thumb/full only (3D) |
paintingImageUrl(painting) |
Local file or on-demand API |