Skill: Backend Route
SkillDev toolsAdding or modifying Elysia API route handlers in src/routes/. Every change must ship with colocated `*.spec.ts` tests covering success and error paths, keep patch coverage ≥ 90% on changed lines, and pass `make fix`, `make test-unit`, `make test-integration`, and `make test-e2e` before submission.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Skill: Backend Route skill
What this skill tells your AI
The instructions your AI receives, as published by exelearning/exelearning in .agents/skills/backend-route/SKILL.md and read by ahel’s review.
Parent: AGENTS.md | Related: backend-service, api-v1
When to Use
Adding or modifying Elysia API route handlers in src/routes/.
Key Files
src/routes/*.ts— route definitions (Elysia plugins with{ prefix })src/routes/*.spec.ts— colocated testssrc/index.ts— route registration (import +.use())src/routes/types/— shared route type definitions
Pattern
// src/routes/my-feature.ts
import { Elysia } from 'elysia';
export const myFeatureRoutes = new Elysia({ prefix: '/api/my-feature' })
.get('/', () => ({ message: 'Hello' }))
.post('/', ({ body }) => ({ received: body }));
// Register in src/index.ts
app.use(myFeatureRoutes);
Test Pattern — app.handle()
Test routes by creating a real Elysia instance and calling app.handle() directly — no HTTP server needed:
import { describe, it, expect } from 'bun:test';
const app = new Elysia().use(myFeatureRoutes);
it('returns 200 for valid request', async () => {
const res = await app.handle(new Request('http://localhost/api/my-feature'));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.message).toBe('Hello');
});
it('returns 401 without auth', async () => {
const res = await app.handle(new Request('http://localhost/api/my-feature/protected'));
expect(res.status).toBe(401);
});
For authenticated routes, create JWT tokens programmatically and pass them as cookies or headers.
Authorization Pattern
Always check ownership before destructive operations:
if (project.owner_id !== currentUser.id) {
set.status = 403;
return { responseMessage: 'FORBIDDEN', detail: 'Only the project owner can delete this project' };
}
Test both success and rejection paths. After a rejected delete, verify the resource still exists.
Commands
bun test src/routes/my-feature.spec.ts # Run route tests
make fix # Lint
Gotchas
- Must register in
src/index.tswith.use()— easy to forget, route silently won't work. - Must have
{ prefix: '/api/...' }in the Elysia constructor — without it, routes mount at root. - Test both authed and unauthed paths — every protected route needs a 401 test and a 403 test (wrong user).
- DI for database queries — routes should call service functions, not query the DB directly. Service functions take
dbas a parameter. - Verify state after failed operations — for delete/reject flows, assert that the resource still exists after a 403 response. Example: PR #1541.
Done When
- Route file with Elysia plugin pattern and
{ prefix } - Registered in
src/index.tsvia.use() -
.spec.tscolocated and passing at 90%+ coverage - Tests cover success, 401 (no auth), and 403 (wrong user) paths
- Database queries use DI pattern (take
dbparameter) -
make fixpasses clean
Signals
- GitHub stars
- 142
- Forks
- 32
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
backend-route- Source
- github.com/exelearning/exelearning