JupiterAPI is an Express read-API over ESN Jupiter (upstream SaaS) that normalizes users, events, and section data for dashboards and exports. It also exposes an optional JWT-gated write proxy (/hercules/*) that records board-managed data (events, fidelity, attendance, approvals) in Firestore.
| Path | Ownership |
|---|---|
src/index.js |
Bootstrap only: middleware chain + route mounting. Zero business logic. |
src/routes/ |
ALL HTTP routers — one file per URL prefix (users.js, events.js, stats.js, auth.js, hercules.js, ...). |
src/middleware/ |
Cross-cutting concerns: apiKeyAuth, rateLimiter, jwtAuth, validation, asyncHandler. |
src/jupiter.js |
Sole upstream Jupiter client (Cognito auth, interceptors, 5-min cache). |
src/config.js |
Sole reader of process.env; fail-fast validation at require time. |
src/nationality.js, src/fieldsOfStudy.js, src/eventLocation.js, src/formatting.js, src/normalizers.js |
Pure domain logic. Never import express or jupiter.js. |
src/googleContacts.js |
Google Contacts CSV row building + streaming. |
test/unit/ |
Characterization tests for pure modules. |
test/routes/ |
HTTP suites with mocked Jupiter/Cognito/Firestore. |
docs/ |
openapi.yaml (API reference), ARCHITECTURE.md, FEATURES.md. |
- Routers own HTTP only; business logic lives in domain modules or
jupiter.js. - No business logic in
index.js. - Only
jupiter.jscalls the upstream API. - Only
routes/hercules.jswrites Firestore. - Environment variables are read only in
config.js. - Board-member authorization uses immutable Cognito
subIDs only (config.boardMemberIds) — never usernames. - Domain modules stay express-free and jupiter-free.
- Keep
docs/openapi.yamlandREADME.mdin sync with any endpoint change.
- New endpoint →
src/routes/<domain>.js+ a row indocs/FEATURES.md+docs/openapi.yaml+ README API overview. - New env var →
src/config.js(+.env.example). Nowhere else. - Upstream payload quirk ("null" strings, 400-means-404) →
jupiter.jsinterceptors ornormalizers.js. - Stats math →
routes/stats.js. - City inference rules →
eventLocation.js/fieldsOfStudy.js. - Nationality/country mapping →
nationality.js.
npm start # run server
npm run dev # run with NODE_ENV=development
npm test # node --test over test/**/*.test.js
npm run lint # eslint .No build step or typecheck — plain CommonJS JavaScript.
- This file (
AGENTS.md) docs/ARCHITECTURE.md- The feature's row in
docs/FEATURES.md - The owning module