add-api-endpoint
Skill130 starsChanged 6 days agoMITView on GitHub
Guide for adding new API endpoints to the Platypus backend using Hono.js — routing, tenant scoping middleware, validation, and database access.
4 other projects use a version of this.
Adopted by
Most recently taken up by
- dream-horizon-org/pulsetheir own version, first seen Oct 8, 2026
- dipu-bd/lightnovel-crawlertheir own version, first seen Oct 8, 2026
- lncrawl/lightnovel-crawlertheir own version, first seen Oct 8, 2026
- Sandhyaa-a/Kaggle_main_Projecttheir own version, first seen Jul 6, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- dipu-bd/lightnovel-crawler★ 2.6ksame name
- lncrawl/lightnovel-crawler★ 2.6ksame name
- dream-horizon-org/pulse★ 44same name
- Sandhyaa-a/Kaggle_main_Project★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository38 other files it gives its agents.
- .agents/skills/humanizer/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- api-conventionsSkill
- ask-mattSkill
- card-conventionsSkill
- codebase-designSkill
- code-reviewSkill
- database-schema-changesSkill
- diagnosing-bugsSkill
- docs-auditSkill
- domain-modelingSkill
- form-conventionsSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- handoffSkill
- humanizerSkill
- implementSkill
- implement-specSkill
- improve-codebase-architectureSkill
- platypus-toolsSkill
- pre-release-checkSkill
- prototypeSkill
- prSkill
- researchSkill
- retroSkill
- setup-matt-pocock-skillsSkill
- tddSkill
- teachSkill
- to-questionnaireSkill
- to-specSkill
- to-ticketsSkill
- triageSkill
- ui-conventionsSkill
- wait-whatSkill
- wayfinderSkill
- wizardSkill
- writing-for-agentsSkill
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 2 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 49
Done when: every route in the module names its scoping middleware explicitly, and you can say which tenant each one is confined to.
- Very specific: line 70
Read the resolved scope through the scope accessors (workspaceScopeOf, orgScopeOf) rather than re-reading path params: the accessor hands …
- Very specific: line 79
Import the shared db instance from the backend entry module, the way the route modules already do, and query it with Drizzle.
- Distinctive: line 83
Never scope a query by a raw path param.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Adding New API Endpoints
Authenticated is not scoped. requireAuth proves who the caller is and
nothing about what they may reach. Platypus is multi-tenant — Organization →
Workspace → resource — so an endpoint carrying only requireAuth is reachable
by any logged-in user against any tenant's data. Scope is a separate,
mandatory decision, taken per route.
Steps
- Create the route module in the backend's routes area — a
Honoinstance exported for mounting. Mount it on the app in the backend's server module. The mount path carries the tenancy, so the hierarchy lives in the URL and the middleware reads the ids from it:
app.route("/organizations/:orgId/workspaces/:workspaceId/agents", agent);- Define the request schemas in the shared schemas package, following the full / create / update variants each domain model already has.
Compose each route: authenticate, scope, validate, handle — in that order, as arguments to the route, not as a blanket
.use("*"). Scope is per route because two routes in one file rarely deserve the same access.agent.post( "/", requireAuth, requireOrgAccess(), requireWorkspaceAccess, validate("json", agentCreateSchema), async (c) => { const data = c.req.valid("json"); const scope = workspaceScopeOf(c); // ... }, );
Done when every route in the module names its scoping middleware explicitly, and you can say which tenant each one is confined to.
Choosing the scope
Pick the narrowest that admits the callers you intend:
| Middleware | Admits |
requireOrgAccess() | any member of the Organization in the path |
requireOrgAccess(["admin"]) | members holding one of the named Organization roles |
requireWorkspaceAccess | callers permitted in the Workspace in the path |
requireWorkspaceOwner | the Workspace's Owner |
requireWorkspaceConfigAccess(null) | credential- or reach-bearing Workspace config, admin-only |
requireWorkspaceConfigAccess(flag) | the same, plus the Owner when that Workspace flag is set |
requireSuperAdmin | platform operators only |
Org and Workspace scoping compose — the Workspace check assumes the Org check ran ahead of it. Super admins bypass the Org membership check by design, so a route may legitimately resolve with no Organization to scope to.
Read the resolved scope through the scope accessors (workspaceScopeOf,
orgScopeOf) rather than re-reading path params: the accessor hands you the
scope the middleware already decided, so the query cannot disagree with the
authorization. Likewise read the session user and Org membership through
userOf and orgMembershipOf, never c.get("user")!: they throw (a 500) when
the middleware is missing from the route.
Database access
Import the shared db instance from the backend entry module, the way the
route modules already do, and query it with Drizzle. The request context also
carries a db, but that is a narrow exception rather than the house pattern.
Never scope a query by a raw path param. Take the tenant ids from the resolved scope, so an unscoped read cannot be written by accident.
Responses
Response body shape — the error key, the message key, and when to throw a
typed error instead of returning one — is covered by the api-conventions
skill. Load it rather than inventing a shape.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

