TypeScript API for NEIS school data (school info, classes, lunch, schedule) and Comcigan timetables.
Production: https://api.timefor.school
Built with Elysia. Interactive reference UI is Scalar (via @elysiajs/openapi).
| Package | Path | Description |
|---|---|---|
timeforschool-api |
repo root | HTTP API (src/) |
@timeforschool/client |
packages/client/ |
NEIS + Comcigan client library (publishable to npm) |
@timeforschool/docs |
apps/docs/ |
Fumadocs site (client + OpenAPI reference) |
Fumadocs + Next.js at apps/docs/:
npm run dev:docs # http://localhost:3000
npm run build:docsWhen API routes or schemas change, refresh the committed OpenAPI artifact and generated MDX:
npm run openapi:syncDeploy on Vercel as two projects (API at repo root, docs at apps/docs). Full plan: docs/vercel-deploy.md.
Agents: see AGENTS.md — run npm run openapi:sync when changing src/ API code.
npm run build -w @timeforschool/client
npm publish -w @timeforschool/client --access publicRequires an npm account with access to the @timeforschool scope. CI publish on GitHub Release uses .github/workflows/publish-client.yml and NPM_TOKEN.
Use the client standalone:
import { NeisClient, fetchTimeTable } from "@timeforschool/client";See packages/client/README.md.
OpenAPI follows Elysia’s OpenAPI pattern: route query / response Typebox schemas, detail summaries, shared models, and plugin config in src/openapi-config.ts. @elysiajs/openapi serves the spec and Scalar UI.
| Resource | URL |
|---|---|
| Scalar API Reference | /docs |
| OpenAPI 3 JSON | /docs/json |
| Service metadata | GET / |
Documentation site: https://docs.timefor.school (Scalar on API: https://api.timefor.school/docs)
- Servers — Production and
localhost:8000are listed in the OpenAPIserversblock and Scalar’s server picker. - Tags — Sidebar groups (Meta, School, Classes, …) from
documentation.tags+ each route’sdetail.tags. - Models — Reusable response shapes (
ApiError,SchoolInfoList, …) are registered with.model()and referenced by name in routeresponsemaps. - Scalar —
layout: modern, default Fetch client, metadata title/description inopenApiPluginConfig.scalar.
Legacy Swagger UI: set provider: 'swagger-ui' on the plugin. Further UI tweaks: Scalar configuration.
npm installCopy .env.example to .env.local for local overrides.
| Variable | Required | Description |
|---|---|---|
NEIS_API_KEY |
No | NEIS API key (falls back to bundled sample key) |
PORT |
No | Local dev port (default 8000) |
npm run dev- App: http://localhost:8000
- Scalar docs: http://localhost:8000/docs
- OpenAPI JSON: http://localhost:8000/docs/json
Preview with the Vercel dev server (same zero-config Elysia detection as production):
npx vercel devErrors use a consistent JSON shape and HTTP status code:
{
"ok": false,
"error": {
"code": "SCHOOL_NOT_FOUND",
"message": "No school matched the given identifier.",
"details": { "schoolname": "..." }
}
}| Code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR |
400 | Invalid or missing query parameters |
CONFLICTING_SCHOOL_PARAMS |
400 | Both schoolname and schoolcode sent |
MISSING_SCHOOL_IDENTIFIER |
400 | Neither schoolname nor schoolcode sent |
SCHOOL_NOT_FOUND |
404 | NEIS has no matching school |
NEIS_DATA_NOT_FOUND |
404 | NEIS returned no rows for the date range |
TIMETABLE_INVALID_GRADE_CLASS |
404 | No Comcigan data for grade/class |
TIMETABLE_AMBIGUOUS_SCHOOL |
409 | Multiple Comcigan matches—use schoolcode |
NEIS_UPSTREAM_ERROR |
502 | NEIS API failure |
TIMETABLE_UPSTREAM_ERROR |
502 | Comcigan fetch/parse failure |
INTERNAL_ERROR |
500 | Unexpected error |
| Route | Description |
|---|---|
GET / |
Service metadata and doc links |
GET /school |
School info (schoolname) |
GET /classes |
Class numbers (grade, schoolname or schoolcode) |
GET /timetable |
Weekly timetable (grade, classno, week, schoolname or schoolcode) |
GET /lunch |
Meal menus (startdate, enddate, schoolname or schoolcode) |
GET /schedule |
School calendar (startdate, enddate, schoolname or schoolcode) |
School identifier: pass exactly one of schoolname or schoolcode (7-digit NEIS code).
Dates: YYYYMMDD (e.g. 20250526).
npm run build— build@timeforschool/client, then compile API todist/npm run build:client— build client package onlynpm start— run compiled server (node dist/server.js)npm test— structural smoke tests
Follow Deploy Elysia on Vercel:
- Default-export the Elysia instance from
src/app.ts(alreadyexport default app). - Import the repo in Vercel and deploy — no
api/folder or framework block invercel.jsonis required for detection. - Set
NEIS_API_KEYfor Production.
npx vercel link
npx vercel env add NEIS_API_KEY
npx vercel deploy --prodThis project uses Node with "type": "module" in package.json. To deploy on the Bun runtime instead, add "bunVersion": "1.x" to vercel.json per the Elysia guide.
Local entry for npm run dev is src/server.ts, which calls app.listen(). Vercel only needs the default export from src/app.ts.