Hyper – an API framework for Bun, distributed as source

mmcclure1 pts0 comments

Hyper — an API framework for Bun, distributed as source<br>AN API FRAMEWORK FOR BUN, DISTRIBUTED AS SOURCE<br>Hyper is an HTTP framework for Bun. There is no @hyper/core in your package.json. The CLI copies the components you want into src/hyper/ as plain TypeScript — yours to read, edit, fork, or delete. hyper diff and hyper update keep the upgrade path intact.<br>Declare a route once. Hyper emits the runtime, an OpenAPI 3.1 document, a typed RPC client, and an MCP server from the same source.

> bun create hyper my-app<br>> bun run devHyper listening on http://localhost:3000

THE REGISTRY<br>The framework is the registry. Every part — router, plugins, test helpers, OpenAPI and MCP adapters — is a folder of source files. Install what you need; the rest never enters your repo. The registry stays an installer, not a runtime dependency.<br>Each install is recorded per-file in hyper.lock.json. Local edits don't block upgrades — hyper update merges only the deltas you accept, the rest of the file stays exactly as you wrote it.

> bun create hyper my-app --template api

> hyper add cors auth-jwt session<br>+ src/hyper/cors/index.ts<br>+ src/hyper/auth-jwt/index.ts<br>+ src/hyper/auth-jwt/jwt.ts<br>+ src/hyper/session/index.ts<br>~ .env.local (1 secret added: JWT_SECRET)

run: bun add zod jose

> hyper diff cors<br>ok src/hyper/cors/index.ts<br>drift src/hyper/cors/options.ts (3 local edits)installed cors, auth-jwt, session + transitive deps. updated hyper.lock.json.

ONE DEFINITION, MANY OUTPUTS<br>A route is one declaration: path, schemas, errors, handler. From it Hyper emits the runtime, an OpenAPI 3.1 document, a typed RPC client, and an MCP server. No parallel schema file, no decorator metadata, no second source of types.<br>Schemas use any Standard Schema library — Zod, Valibot, Arktype — directly. Errors declared on the route appear as response codes in OpenAPI, a discriminated union in the client, and assertions in the test runner.

import { Hyper, ok } from "@hyper/core"<br>import { z } from "zod"

export default new Hyper()<br>.get("/health", "OK")<br>.post(<br>"/users",<br>body: z.object({<br>name: z.string().min(1),<br>email: z.email(),<br>}),<br>throws: { 409: z.object({ taken: z.string() }) },<br>},<br>async ({ body }) => {<br>return ok({ id: crypto.randomUUID(), ...body })<br>},<br>.listen(3000)<br>> hyper openapi > openapi.json # 3.1 spec<br>> hyper client ./client.gen.ts # typed RPC client<br>> hyper mcp # MCP server, no extra configopenapi 3.1 written -> openapi.json (12 routes, 5 schemas)

PLUGINS AND SUB-APPS<br>One composition primitive: .use(). It takes a plugin, a sub-app with its own prefix, an imported route file, or a function that decorates the request. The router flattens at build time — nesting is organisational, free at runtime.<br>In the registry today:<br>cors — CORS, with strict wildcard rejection when credentials are present<br>auth-jwt — JWT auth, EdDSA / RS256 / HS256, 32-byte secret floor<br>session — encrypted cookie sessions, CSRF double-submit<br>log — structured logging, header / body redaction<br>rate-limit — sliding window and token bucket, per-route override<br>csp — Content-Security-Policy, report-only mode, nonces<br>cache — RFC 9111-aware route caching with conditional revalidation<br>idempotency — idempotency-key handling for write methods<br>otel — OpenTelemetry traces and metrics

import { Hyper } from "@hyper/core"<br>import { corsPlugin } from "@hyper/cors"<br>import { logPlugin } from "@hyper/log"<br>import { authJwtPlugin } from "@hyper/auth-jwt"<br>import { rateLimitPlugin } from "@hyper/rate-limit"

import users from "./routes/users.ts"<br>import posts from "./routes/posts.ts"

export default new Hyper()<br>.use(logPlugin())<br>.use(corsPlugin({ origin: ["https://example.com"] }))<br>.use(rateLimitPlugin({ window: "1m", max: 60 }))<br>.use(authJwtPlugin())<br>.use(users) // honors its own prefix -> /users/*<br>.use("/v1", posts) // re-prefixed -> /v1/posts/*<br>.listen(3000)

BUN-NATIVE<br>Built for Bun directly — HTTP server, cookie map, password and hash APIs, file primitives. A thin layer on a fast runtime, not an abstraction over runtimes you don't ship to.<br>Handlers pay only for what they read. Static responses bypass the handler path. Security defaults — HSTS, body limits, strict CORS, secret-length floors, CSRF double-submit — are audited by hyper security --check in CI.

> hyper security --check

ok HSTS enabled in production<br>ok body limit = 32 bytes<br>ok session secret >= 32 bytes<br>ok CSRF double-submit on state-changing methods<br>... 5 moreok - secure-by-default posture intact (12/12 checks).

ONE BINARY<br>One CLI for the whole loop: scaffold, dev (hot reload + type-check), test (.example() contracts + bun:test), bench, build, and the registry (add, diff, update, list).<br>Every command emits JSON with --json. Editor integrations, CI, and agents read the same surface you do.

hyper init [template] scaffold + auto-install core<br>hyper dev [entry] hot reload + tsgo --watch<br>hyper build [entry] bundle + route graph<br>hyper test .example() contracts + bun:test<br>hyper bench [entry] per-route latency p50/p99<br>hyper openapi [out] OpenAPI...

hyper from cors openapi import route

Related Articles