Why it exists
The OpenSource Together API is a closed-source service. Rather than making contributors ask for credentials, the web app ships a local mock of the entire API contract, seeded with realistic data..env, no database, no Docker, no OAuth app.
This is not the browser service-worker flavour of MSW. It’s a real HTTP server: an
Express app serving MSW handlers on port
4000, so server components,
generateMetadata, and the sitemap hit it too, exactly like the real API.The files
Everything lives insrc/mocks/:
Sessions without OAuth
The mock issues the same cookie name the real API uses (better-auth.session_token), which is
exactly what src/middleware.ts checks for. That’s why protected routes work with no OAuth
flow at all.
You start signed in. To test signed-out states, use the Mock mode button in the
bottom-left corner of the page. It toggles a mock_signed_out cookie and reloads.
Try it
Toggle to Signed out, then visit
/dashboard/my-projects. You should be redirected to
the login page with a redirectTo parameter.Seeded state
db.mock.ts clones the fixtures at boot and arranges a few things so every flow is reachable:
- three projects are owned by the mock user, so the dashboard isn’t empty
- one project has no owner, so claiming can be tested
- one project is bookmarked, so the bookmarks list isn’t empty
- uploaded images are kept in memory and served from
/mock-uploads/:id
Adding a handler
When you build a feature that calls an endpoint the mock doesn’t cover, the request returns 501 and the terminal logs:src/mocks/handlers.mock.ts.
Response helpers
Use the helpers. They produce the exact envelopes the real API returns, which is what theapiData() client expects.
An example
api(path)wraps the path as*${path}so the handler matches regardless of host. Always use it.- Check auth first. If the real endpoint is guarded, the mock should be too. Otherwise you’ll build a flow that breaks in production.
Add seed data if you need it
New fields go insrc/mocks/fixtures/. Project data lives in
fixtures/projects.mock.json, which is large. Biome deliberately skips formatting it.
Add a test
If you change mock behaviour or state, add a case tosrc/mocks/handlers.mock.test.ts:
msw/node with onUnhandledRequest: "error", and resets
the database before each test.
Deliberate gaps
Some things aren’t reproduced locally, on purpose:
There’s also one route that exists only in the mock:
GET /mock-uploads/:id, which serves
uploaded bytes back. Don’t build against it. It doesn’t exist in the real API.
Extending it is a contribution
The mock is deliberately incomplete: handlers are added when a feature needs them. If you hit a 501, adding the handler is a welcome pull request. The server even says so in its own error message.Contribution workflow
Branches, commits, and what CI checks.