Spin up the local PostgreSQL container (defaults: user postgres, password postgres, db antenna on port 5432):
docker compose up -d dbOverride any credential via environment variables if needed:
POSTGRES_USER=myuser POSTGRES_PASSWORD=secret POSTGRES_DB=mydb docker compose up -d dbpnpx prisma migrate dev --name init./populate.shRequires
jqto be installed and adata/stations.jsonfile to be present.
pnpm install
pnpm devStart the full stack (PostgreSQL + app) with a single command:
docker compose up -dThen seed the database with station data:
./populate.shThe app will be available at http://localhost:3000.
docker compose down -v # removes the volume
docker compose up -d db
pnpx prisma migrate dev --name init
./populate.shReleases are automated with Release Please. Each release bumps the version in package.json, creates a git tag (e.g. v0.1.1), and publishes a GitHub Release.
Use Conventional Commits on PRs merged to main:
feat:β new feature (patch bump while below1.0.0)fix:β bug fix (patch bump)feat!:orBREAKING CHANGE:β breaking change (minor bump while below1.0.0)chore:,docs:,refactor:β no version bump unless paired with a releasable change
- Merge feature/fix PRs to
mainusing conventional commit titles. - Release Please opens or updates a Release PR with the next version,
CHANGELOG.md, andpackage.jsonbump. - Review and merge the Release PR.
- Release Please creates the git tag and GitHub Release automatically.
The version shown on the About page comes from package.json and updates after the release PR is merged and deployed.
Returns a paginated list of radio stations.
| Parameter | Type | Default | Description |
|---|---|---|---|
pageSize |
number | 20 |
Items per page (max 100) |
cursor |
string | β | Opaque cursor from a previous response's pagination.nextCursor |
sortBy |
name | country |
name |
Field to sort by |
order |
asc | desc |
asc |
Sort direction |
country |
string | β | Filter by exact country name |
language |
string | β | Filter by exact language |
tag |
string | β | Filter stations that contain this tag |
{
"data": [
{
"id": "018edc4f-...",
"name": "Arrow Classic Rock",
"country": "The Netherlands",
"language": "dutch",
"streamUrl": "http://...",
"homepageUrl": "https://...",
"logoUrl": "https://...",
"tags": ["classic rock", "rock"]
}
],
"pagination": {
"nextCursor": "MDE4ZWRj...",
"nextPage": "http://localhost:3000/api/radios/stations?pageSize=20&cursor=MDE4ZWRj...",
"previousPage": "http://localhost:3000/api/radios/stations?pageSize=20&cursor=MDAwMDAw...",
"hasMore": true,
"pageSize": 20
}
}nextCursorβ pass ascursorin the next request to fetch the following page;nullwhen there are no more results.nextPageβ URL to fetch the following page;nullwhen there are no more results.previousPageβ URL to fetch the previous page;nullwhen there are no previous results.hasMoreβtrueif another page exists.
# First page, 10 results sorted by name
curl "http://localhost:3000/api/radios/stations?pageSize=10"
# Next page using cursor from previous response
curl "http://localhost:3000/api/radios/stations?pageSize=10&cursor=<nextCursor>"
# Filter by country, sorted descending
curl "http://localhost:3000/api/radios/stations?country=Germany&sortBy=name&order=desc"
# Filter by tag
curl "http://localhost:3000/api/radios/stations?tag=jazz"