Skip to main content

OpenAPI

The API describes itself with Elysia schema annotations (detail, query, body, response) on each route. The OpenAPI spec is generated from those annotations, committed at apps/api/openapi.json, and served by the Worker itself.


Where to find it​

URLWhat
https://api.riftseer.com/docsInteractive reference, powered by Scalar
https://api.riftseer.com/api/v1/openapi.jsonThe spec the reference reads

Both work against wrangler dev too (http://localhost:8789/docs), and Scalar's "try it" requests go to whichever origin served the page.


One source​

apps/api/src/app.ts exports buildApp(provider, options), the complete route composition. The Worker calls it with the Cloudflare adapter; apps/api/scripts/generate-spec.ts calls it under plain Bun with the in-memory stub provider and mounts @elysiajs/swagger beside it to extract the spec. Because both go through the same function, a mounted route is in the spec by construction — there is no second list to forget a route in.

The generator needs no credentials and nothing time- or environment-dependent reaches the JSON, so rerunning it on an unchanged tree is byte-identical.

cd apps/api
bun run generate:spec # rewrites apps/api/openapi.json

bun run spec:check (part of bun run check, so CI runs it) regenerates and fails on any diff. When a route contract changes, regenerate and commit the spec in the same PR.

@elysiajs/swagger stays a dev dependency: the Worker serves the committed JSON as a bundled import and one static HTML string, and never loads the plugin.


Updating what the reference shows​

Edit the detail object and schemas in the relevant route file:

// apps/api/src/routes/cards.ts
.get("/cards/random", handler, {
response: { 200: CardSchema, 404: ErrorSchema },
detail: {
tags: ["Cards"],
summary: "Get a random card",
description: "Returns a single random card, with its preferred printing.",
},
})
AnnotationWhat it controls
detail.summaryOne-line label shown in the endpoint list
detail.descriptionLonger description shown when the endpoint is expanded
detail.tagsWhich group the endpoint appears under
detail.hideLeaves the route out of the spec entirely
query / bodyRequest parameter and body schemas (types + descriptions)
responseResponse schemas per status code

To describe an individual parameter, pass it in the schema:

query: t.Object({
name: t.Optional(t.String({ description: "Card name to search for" })),
});

Tags are declared in the documentation.tags array in scripts/generate-spec.ts. Add a tag there first, then reference it by name from detail.tags.


Eden Treaty​

App in src/app.ts (re-exported from src/index.ts) exposes all route types for Eden Treaty clients. This is independent of the OpenAPI spec — Eden uses TypeScript inference at compile time, not the runtime spec.

The Discord bot (apps/discord-bot/src/api.ts) and the website consume it:

import type { App } from "@riftseer/api";