@geektech/backone
v0.1.2
Published
BackOne 是基于 Bun 的轻量级纯 TypeScript 服务端框架,零运行时依赖,提供路由、中间件、上下文与静态文件服务
Maintainers
Readme
BackOne
English | 简体中文
A lightweight server-side framework written entirely in TypeScript, built on Bun. Zero runtime dependencies — routing, middleware, request context, and static file serving.
Features
- TypeScript-first with zero runtime dependencies
- Bun-native: built on
Bun.serve, streaming responses, zero-copy static files - Segment radix-tree router with
:paramand*wildcard support - Typed routes: generic params give IDE autocompletion and type checking
- Onion-model middleware chain with a typed per-request
Context - Lazy query / body parsing with caching
- Return a
Responsedirectly for zero-overhead passthrough - Built-in middleware:
logger,cors,gzip,serveStatic,timeout,helmet - Static files with ETag, Last-Modified, If-None-Match 304 and Range 206
- WebSocket support via
app.ws(path, handler)on Bun native upgrade - Route groups with
app.group(prefix, callback)for shared prefixes - Route prefix with
app.usePrefix(prefix, callback?): persistent replacement or scoped stacking for later routes, composable withgroup - Graceful shutdown: wait for in-flight requests, force-close after timeout
Requirements
- Bun >= 1.3
Quick Start
import { createServer } from '@geektech/backone';
const app = createServer({ port: 3000 });
app.useLogger();
app.get('/users/:id', (ctx) => ctx.json({ id: ctx.params.id }));
app.post('/echo', async (ctx) => ctx.json(await ctx.bodyJson()));
app.serveStatic('/public', './public');
await app.listen();API
createServer(options)
Create a server application. Options: port (default 3000, 0 for a random
port), hostname (default 0.0.0.0), development (log error details and
expose messages in error responses), maxRequestBodySize, idleTimeout.
Routing
app.get / post / put / patch / delete / options / head / all(path, ...handlers)
register handlers. Paths support static segments, :param, and * wildcards.
Multiple handlers on one route run in order.
All route methods accept a generic type for typed params:
app.get<{ id: string }>('/users/:id', (ctx) => {
// ctx.params.id is typed as string
return ctx.json({ id: ctx.params.id });
});Route groups register multiple routes under a shared prefix:
app.group('/api/v1', (api) => {
api.get('/users', handler);
api.post('/users', handler);
});usePrefix adds a prefix to routes registered afterwards — persistent or
scoped, composable with group:
app.usePrefix('/api');
app.get('/users', handler); // GET /api/users
app.usePrefix('/v2', (api) => {
api.get('/users', handler); // GET /api/v2/users
}); // prefix restored to /api afterwards
app.usePrefix(); // reset: no prefixContext
Each request gets a Context:
ctx.params,ctx.query— path / query parameters (query is lazily parsed)ctx.bodyJson<T>(),ctx.bodyText()— lazily parsed and cached bodyctx.json / text / html / stream / redirect / noContent— response buildersctx.status,ctx.set(name, value),ctx.headersctx.cookie(name),ctx.setCookie(name, value, options)
Handlers may return a Response directly (passthrough) or a primitive / object
which the framework serializes.
Middleware
app.use(...handlers) installs middleware; the chain follows the onion model.
Built-in: logger, cors, gzip, timeout, helmet, serveStatic (also
available as app.useLogger(), app.useCors(), app.useGzip(),
app.useTimeout(), app.useHelmet(), app.serveStatic()).
serveStatic serves files via Bun.file with zero-copy streaming. Responses
carry ETag, Last-Modified and Accept-Ranges: bytes; If-None-Match /
If-Modified-Since return 304, and Range requests return 206 partial
content.
helmet sets common security headers (X-Content-Type-Options, X-Frame-Options,
Referrer-Policy, etc.), each overridable or disablable via options. timeout
returns 504 when a handler exceeds the specified milliseconds.
WebSocket
Register a WebSocket handler on a path with app.ws(path, handler). Requests
with Upgrade: websocket on a registered path are automatically upgraded via
Bun native server.upgrade().
app.ws('/echo', {
open(ws) { ws.send('connected'); },
message(ws, message) { ws.send(`echo: ${message}`); },
close(ws, code, reason) { console.log('closed', code, reason); },
});Errors
Throw HttpError from @geektech/backone to control the status code; unknown
errors become 500 Internal Server Error without leaking internals.
Development
bun test # run tests
bun run build # build dist (Bun.build + type declarations)
bun run lint # eslintRepository Structure
This package lives at packages/backone in the BackOne monorepo
(@geektech/backone is the server-side framework; @geektech/backone-utils
is its general-purpose toolkit; @geektech/backone-cli scaffolds server
applications). Publish checks follow the same discipline:
bun test && bunx tsc --noEmit && bun run build && bun pm pack --dry-run.
License
MIT
