A playground for my personal API projects.
- TypeScript 92.8%
- CSS 4.7%
- JavaScript 2.5%
| .agents/skills | ||
| .forgejo/workflows | ||
| .vscode | ||
| apps/web | ||
| packages | ||
| .eslintrc.js | ||
| .gitignore | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| skills-lock.json | ||
| tsconfig.json | ||
| turbo.json | ||
relay
A playground for my personal API projects. Each project lives in the sidebar and has its own page where you can browse its endpoints and hit them straight from the browser.
Stack
- Next.js 16 (App Router, TypeScript) —
apps/web - Tailwind CSS v4 + shadcn/ui (Base UI "base-vega" style) —
packages/ui - pnpm workspaces + Turborepo
Getting started
pnpm install
pnpm dev
The web app runs at http://localhost:3000.
Scripts
Run from the repo root:
| Command | What it does |
|---|---|
pnpm dev |
Start the dev server (turbo) |
pnpm build |
Production build |
pnpm lint |
ESLint across packages |
pnpm typecheck |
TypeScript across packages |
pnpm format |
Prettier per package (**/*.{ts,tsx}) |
Adding a project
Projects live in apps/web/lib/projects.ts. Add an entry to the projects array:
{
slug: "docs-api",
name: "docs-api",
tagline: "Document extraction API",
description: "Converts remote documents to Markdown.",
baseUrl: "https://docs-api.tbdh.app",
icon: "doc", // key in the `projectIcons` map, same file
endpoints: [
{
id: "health",
method: "GET",
path: "/",
summary: "Health check",
description: "Returns a short description string.",
},
{
id: "extract",
method: "POST",
path: "/extract",
summary: "Convert a document to Markdown",
description: "Fetches `file_url` and returns Markdown.",
// Prefilled as the request body in the playground:
requestBody: { file_url: "https://example.com/document.pdf" },
},
],
}
Notes:
pathcan contain{param}slots;sampleParamsfills them (e.g./api/{code}with{ code: "a1b2c3" }).- An endpoint shows a request-body editor when
requestBodyis set; responses render as highlighted JSON when parseable, plain text otherwise. - Add new icons by extending the
projectIconsmap.
How requests work
Side projects usually don't send CORS headers, and browsers block cross-origin requests without them. The playground sends requests to a same-origin route — apps/web/app/api/proxy/route.ts — which forwards the method, body, and content-type upstream:
- Only http(s) targets; 25s upstream timeout, 20s client timeout
- Returns 502 with a message when the upstream can't be reached
History
Completed requests are persisted to localStorage under relay:history (newest 6) and restored on reload. The trash button in the History card clears them.
Projects
- pdf-tools-api — PDF extraction and conversion API (docs, live). PDFs must be under 5 MB;
/extractand/converteach fetch the file from a URL. - docs-api — document extraction API (docs, live). Note: rate-limited to 15 requests/min;
/extractcan take ~10s on large files. - kuryana — MyDramaList scraper API (docs, live, swagger). Scrapes on the go with no caching, so requests can be slow;
/seasonaland/scheduleproxy MDL's own calendar endpoints and can 500.
Links
- Website: https://www.tbdh.dev/
- GitHub: https://github.com/tbdsux
- Git server: https://git.tbdh.app/