Skip to content

Latest commit

 

History

33 Commits

Folders and files

Repository files navigation

Snapaper Node.js

The Node.js back-end of Snapaper


How it works

The API lists CAIE subjects, exam sessions and papers by reading PapaCambridge. Paper URLs point straight at PapaCambridge, so the API never proxies files.

Every listing it reads is kept in a last-known-good cache:

  • A listing is fresh for 12 hours. After that it is still served instantly while one background request refreshes it.
  • A failed refresh keeps serving the old copy and retries 10 minutes later. Errors and empty pages are never cached.
  • Redis shares the cache between machines and keeps it through restarts and deploys. An in-memory copy keeps serving while Redis is down.

As a result, anything that has been viewed before keeps working while PapaCambridge is down. Only listings nobody has requested yet return 502.


Endpoints

Route Response
GET /api/cates/ppca/:level Subjects for as-and-a-level or igcse
GET /api/years/ppca/:level/:subject Exam sessions for a subject, newest first
GET /api/papers/ppca/:level/:subject/:session Papers for a session
GET /healthz ok, without touching Redis or PapaCambridge

:subject only needs to end with the 4-digit syllabus code, e.g. physics-(9702).


Development

Requires Node.js 22 or later and pnpm.

git clone git@github.com:Snapaper/snapaper-nodejs.git
pnpm install
pnpm dev

The API listens on port 8080 unless PORT is set. Redis is optional: put REDIS_URL=redis://... in a .env file to use one, or run without it and the in-memory cache takes over.


Deployment

The API runs on Fly.io with one machine in Singapore (sin) and one in Frankfurt (fra). Fly routes each user to the nearest healthy machine and stops sending traffic to a machine that fails its /healthz check, so either machine can go down without an outage.

fly deploy

Deploys roll one machine at a time and wait for the health check before moving on. The Redis cache is a Fly-managed Upstash database whose URL is stored in the REDIS_URL secret.

To restore the two-region layout, set each region separately:

fly scale count 1 --region sin
fly scale count 1 --region fra

Sponsor this project

Used by

Contributors

Languages