# Website API v2 — `/api/v2` Skeleton (PR 1) > ## ⚠ Superseded — not scheduled > > The router domain split is being done **in place**, with every URL byte-identical, so there is no > parallel version to stand up and this scaffold will not be built. See > [API_V2_PLAN.md](./API_V2_PLAN.md) § Why there is no `/api/v2`. > > The file is kept, unedited below, as the concrete recipe **if** a real contract break ever forces a > versioned API. Nothing here describes current or planned work. Companion to [API_V2_PLAN.md](./API_V2_PLAN.md) — this is the concrete scaffold for **PR 1** in that plan's sequencing. It stands up `/api/v2` **empty but wired**, next to a frozen `/api/v1`, with **no behavior change**. Endpoints are filled in by the later PRs (auth merge, then the domain split). ## Scope - Create the `router/v2/` tree of empty, domain-named routers. - Mount `/v2` alongside `/v1` in `api.router.js`. - Add a single trivial `GET /api/v2/version` so the mount is testable end-to-end. - **Out of scope:** any real endpoint, any auth change, any controller edit. Those are PR 2+. ## File tree to create ``` website/server/src/router/v2/ v2.router.js # mounts the domain sub-routers; adds GET /version admin/ index.js # mounts the admin capability routers under /admin dashboard.router.js users.router.js moderation.router.js content.router.js wiki.router.js shard.router.js settings.router.js invites.router.js bot-activity.router.js auth/ index.js login.router.js sso.router.js totp.router.js session.router.js public/ index.js news.router.js wiki.router.js page.router.js shard.router.js player/ index.js profile.router.js appeals.router.js shard.router.js ``` `internal/` is **not** part of v2's public tree — the internal routes stay on the separate, unpublished port (`internalApp.js`), exactly as in v1. See `API_V2_PLAN.md` § Phase 2. ## Wiring `api.router.js` gains the v2 mount next to v1: ```js const v1Router = require('./v1/v1.router') const v2Router = require('./v2/v2.router') apiRouter.use('/v1', v1Router) apiRouter.use('/v2', v2Router) // NEW — parallel version, migrate off v1 route-by-route ``` `v2.router.js` mounts each domain group and exposes the version ping: ```js const express = require('express') const v2Router = express.Router() const adminRouter = require('./admin') const authRouter = require('./auth') const publicRouter = require('./public') const playerRouter = require('./player') // Cheap liveness/mount check so the parallel version is testable before any // real endpoint exists. Returns the API major version, nothing sensitive. v2Router.get('/version', (req, res) => res.json({ version: 2 })) v2Router.use('/auth', authRouter) v2Router.use('/public', publicRouter) v2Router.use('/admin', adminRouter) v2Router.use('/player', playerRouter) // NOTE: /internal is intentionally NOT mounted here — same reason as v1. module.exports = v2Router ``` Each capability router is an empty stub at this stage — a router that mounts cleanly and adds no routes yet, so PR 2+ only has to add handlers, never re-wire: ```js // router/v2/admin/dashboard.router.js const express = require('express') const router = express.Router() // Routes added in the admin domain-split PR (see API_V2_PLAN.md § Phase 2). module.exports = router ``` Each `index.js` mounts its group's capability routers under the URL that names them, e.g.: ```js // router/v2/admin/index.js const express = require('express') const admin = express.Router() admin.use('/dashboard', require('./dashboard.router')) admin.use('/users', require('./users.router')) admin.use('/moderation', require('./moderation.router')) admin.use('/content', require('./content.router')) admin.use('/wiki', require('./wiki.router')) admin.use('/shard', require('./shard.router')) admin.use('/settings', require('./settings.router')) admin.use('/invites', require('./invites.router')) admin.use('/bot-activity', require('./bot-activity.router')) module.exports = admin ``` ## Acceptance criteria - Server boots with no error; every sub-router mounts. - `GET /api/v2/version` → `200 { "version": 2 }`. - `GET /api/v1/**` behavior is **byte-for-byte unchanged** — v1 is untouched. - Existing server tests stay green (`cd website/server && npm test`). - A new test asserts the `/api/v2/version` mount (smallest possible coverage of the wiring). ## Docs / spec - Swagger regeneration is deferred until v2 has real routes (PR 2) — a lone `/version` ping doesn't need an annotation. When PR 2 lands, add `#swagger.*` to the new routes and run `npm run swagger`. - No `BACKEND_DESIGN.md` change here beyond noting the parallel `/api/v2` mount exists; the route-map/security-contract edits land with the PRs that add real endpoints. ## Next PR 2 fills the `auth/` routers with the bearer access + refresh flow and drops the session cookie — see [API_V2_PLAN.md](./API_V2_PLAN.md) § Phase 1.