Elysia API with TypeBox validation and guards, tested via app.handle on Node: working versions (tested 2026-10-15)
An Elysia 1.4 app with TypeBox schemas for params, query, body and responses, a guard with an API key check and custom error mapping, tested on Node with web-standard Request objects through app.handle and no open port.
passed5 passed · 0 failed2026-10-15 14:57 UTC- Status
- passed
- Last passed
- 2026-10-15 14:57 UTC
- Tests
- 5 / 5
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions7
elysia@1.4.30@sinclair/typebox@0.34.52exact-mirror@1.2.6file-type@22.2.0openapi-types@12.1.3tsx@4.23.15@types/node@26.6.5
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38755782594
The code
Exactly the files that passed. Your agent can pull them with get_recipe("elysia-typebox-handle-tests") (install), or see them on GitHub.
package.json
{
"name": "elysia-typebox-handle-tests",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"elysia": "1.4.30",
"@sinclair/typebox": "0.34.52",
"exact-mirror": "1.2.6",
"file-type": "22.2.0",
"openapi-types": "12.1.3"
},
"devDependencies": {
"tsx": "4.23.15",
"@types/node": "26.6.5"
}
}
src/app.ts
import { Elysia, t } from "elysia";
type Note = { id: number; title: string; body: string; tags: string[] };
export class NoteNotFound extends Error {
status = 404;
}
/**
* An Elysia app with TypeBox schemas on params, query, body and response. Tests call
* app.handle(new Request(...)) with web-standard Request objects, so no port is opened
* and the same code runs on Bun or Node.
*/
export function createApp(apiKey = "test-key") {
const notes = new Map<number, Note>();
let nextId = 1;
const NoteSchema = t.Object({ id: t.Number(), title: t.String(), body: t.String(), tags: t.Array(t.String()) });
return new Elysia()
.error({ NOTE_NOT_FOUND: NoteNotFound }) // registered errors get their own onError code
.onError(({ code, error, set }) => {
if (code === "VALIDATION") { set.status = 422; return { error: "validation", detail: error.all.map((e) => ("path" in e ? e.path : "")) }; }
if (code === "NOTE_NOT_FOUND") { set.status = 404; return { error: "note not found" }; }
})
.get("/health", () => ({ ok: true }))
.guard({
// Runs before every route in this group: a simple API key check.
beforeHandle: ({ headers, set }) => {
if (headers["x-api-key"] !== apiKey) { set.status = 401; return { error: "unauthorized" }; }
},
}, (app) => app
.post("/notes", ({ body, set }) => {
const note = { id: nextId++, ...body, tags: body.tags ?? [] };
notes.set(note.id, note);
set.status = 201;
return note;
}, {
body: t.Object({ title: t.String({ minLength: 1, maxLength: 120 }), body: t.String(), tags: t.Optional(t.Array(t.String({ pattern: "^[a-z-]+$" }), { maxItems: 5 })) }),
response: { 201: NoteSchema },
})
.get("/notes", ({ query }) => {
const list = [...notes.values()].filter((n) => !query.tag || n.tags.includes(query.tag));
return list.slice(0, query.limit);
}, { query: t.Object({ tag: t.Optional(t.String()), limit: t.Number({ default: 10, minimum: 1, maximum: 50 }) }) })
.get("/notes/:id", ({ params }) => {
const n = notes.get(params.id);
if (!n) throw new NoteNotFound(`note ${params.id}`);
return n;
}, { params: t.Object({ id: t.Numeric() }) }),
);
}
test/app.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { createApp } from "../src/app.ts";
const KEY = { "x-api-key": "test-key", "content-type": "application/json" };
const req = (path: string, init: RequestInit = {}) => new Request(`http://localhost${path}`, init);
test("public route works without a key", async () => {
const res = await createApp().handle(req("/health"));
assert.equal(res.status, 200);
assert.deepEqual(await res.json(), { ok: true });
});
test("guarded routes reject a missing API key", async () => {
const res = await createApp().handle(req("/notes"));
assert.equal(res.status, 401);
assert.deepEqual(await res.json(), { error: "unauthorized" });
});
test("creates a note and coerces path and query params", async () => {
const app = createApp();
const created = await app.handle(req("/notes", { method: "POST", headers: KEY, body: JSON.stringify({ title: "Ship", body: "today", tags: ["work"] }) }));
assert.equal(created.status, 201);
assert.deepEqual(await created.json(), { id: 1, title: "Ship", body: "today", tags: ["work"] });
await app.handle(req("/notes", { method: "POST", headers: KEY, body: JSON.stringify({ title: "Rest", body: "later" }) }));
const one = await app.handle(req("/notes/1", { headers: KEY }));
assert.equal((await one.json()).title, "Ship");
const tagged = await (await app.handle(req("/notes?tag=work&limit=5", { headers: KEY }))).json();
assert.deepEqual(tagged.map((n: { id: number }) => n.id), [1]);
const limited = await (await app.handle(req("/notes?limit=1", { headers: KEY }))).json();
assert.equal(limited.length, 1);
});
test("TypeBox validation failures map to 422 with paths", async () => {
const app = createApp();
const bad = await app.handle(req("/notes", { method: "POST", headers: KEY, body: JSON.stringify({ title: "", body: 3, tags: ["Bad Tag"] }) }));
assert.equal(bad.status, 422);
const { error, detail } = await bad.json();
assert.equal(error, "validation");
for (const p of ["/title", "/body", "/tags/0"]) assert.ok(detail.includes(p), `missing ${p} in ${detail}`);
const badQuery = await app.handle(req("/notes?limit=500", { headers: KEY }));
assert.equal(badQuery.status, 422);
const badParam = await app.handle(req("/notes/abc", { headers: KEY }));
assert.equal(badParam.status, 422);
});
test("unknown note ids return 404", async () => {
const res = await createApp().handle(req("/notes/99", { headers: KEY }));
assert.equal(res.status, 404);
assert.deepEqual(await res.json(), { error: "note not found" });
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.