zoltraak
v0.0.6
Published
Ultra-performance secure web framework for Bun with Express-like API, built-in security, and zero-cost abstractions
Downloads
44
Maintainers
Readme
Zoltraak
Ultra-performance secure web framework for Bun.
import { Zoltraak } from 'zoltraak'
const app = new Zoltraak()
app.get('/ping', () => 'pong')
app.get('/users/:id', (ctx) => ({ id: ctx.params.id }))
app.delete('/users/:id', () => null) // → 204 No Content
app.listen(3000)Why Zoltraak
- Routes compiled at startup — zero overhead at runtime, all optimizations happen once
- Auto return detection — return a string, object, or
null; the framework picks the rightContent-Typeand status code - Typed path params —
ctx.params.idis a compile error on a route that has no:id - Built-in security — security headers, rate limiting, and body limits out of the box
- Guards — composable auth/authz functions that run before handlers
- WebSocket — first-class, with typed per-connection data
- Zero dependencies — only Bun and TypeScript
Install
bun add zoltraakRequires Bun ≥ 1.2.3.
Return anything
Handlers can return a value directly — no need to call ctx.json() or new Response() unless you want to.
app.get('/text', () => 'Hello world') // text/plain 200
app.get('/json', () => ({ ok: true })) // application/json 200
app.get('/empty', () => null) // 204 No Content
app.get('/custom', (ctx) => {
ctx.status(201)
ctx.set('X-Created', 'true')
return { id: 42 } // application/json 201 + header
})
app.get('/passthrough', () =>
new Response('raw', { status: 200 }) // passed through as-is
)| Return value | Response |
|---|---|
| string | text/plain; charset=utf-8 with ctx status |
| object / array / number / boolean | application/json with ctx status |
| null / undefined | 204 No Content |
| Response | passed through (ctx headers merged in) |
Path parameters
app.get('/users/:id', (ctx) => {
ctx.params.id // ✅ string
ctx.params.foo // ❌ TypeScript error — 'foo' doesn't exist on this route
})
app.get('/posts/:postId/comments/:commentId', (ctx) => {
const { postId, commentId } = ctx.params // both typed as string
return { postId, commentId }
})Query parameters
app.get('/search', (ctx) => ({
q: ctx.queryParam('q'), // string | null
page: ctx.queryParam('page', '1'), // string — default '1', never null
limit: ctx.queryParam('limit', '10'),
}))Guards
Guards run before the handler. Return true to allow, false for 403 Forbidden, or a custom Response.
import type { Guard } from 'zoltraak'
const authGuard: Guard = (ctx) => ctx.bearerToken() !== null
const adminGuard: Guard = async (ctx) => {
const user = await db.getUser(ctx.bearerToken()!)
return user?.role === 'admin'
}
app.get('/protected', handler, [authGuard])
app.get('/admin', handler, [authGuard, adminGuard])Compose guards with boolean logic:
import { composeGuardsAnd, composeGuardsOr, negateGuard } from 'zoltraak'
const canEdit = composeGuardsAnd(authGuard, composeGuardsOr(adminGuard, ownerGuard))
app.put('/posts/:id', handler, [canEdit])Middleware
app.use(async (ctx, next) => {
const start = Date.now()
const res = await next()
console.log(`${ctx.method} ${ctx.path} — ${Date.now() - start}ms`)
return res
})Route groups
app.group('/api/v1', (api) => {
api.use(authMiddleware)
api.get('/users', listUsers) // GET /api/v1/users
api.post('/users', createUser) // POST /api/v1/users
api.get('/users/:id', getUser) // GET /api/v1/users/:id
})Body parsing & validation
import { parseBody, safeParseBody, t } from 'zoltraak'
app.post('/users', async (ctx) => {
// throws ValidationError on bad input — caught by onError
const data = await parseBody(ctx, {
name: t.string({ minLength: 1 }),
email: t.string(),
age: t.optional(t.number({ min: 0 })),
})
// data: { name: string, email: string, age?: number }
ctx.status(201)
return data
})
// Safe variant — no throw
app.post('/safe', async (ctx) => {
const result = await safeParseBody(ctx, schema)
if (!result.ok) return ctx.badRequest(result.error.message)
return result.data
})Error handling
import { ValidationError } from 'zoltraak'
app.onError((err, ctx) => {
if (err instanceof ValidationError) return ctx.badRequest(err.message)
console.error(err)
return ctx.internalError()
})WebSocket
app.ws('/chat', {
upgrade(ctx) {
const token = ctx.queryParam('token')
if (!token) return null // reject
return { userId: verify(token) } // attach to connection
},
open(ws) { ws.subscribe('room') },
message(ws, msg) { ws.publish('room', msg) },
close(ws) { console.log('bye', ws.data.data.userId) },
})CORS
import { cors } from 'zoltraak'
app.use(cors({
origin: ['https://example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}))Static files
app.static('/public', './public')
// GET /public/logo.png → ./public/logo.pngRate limiting
import { createRateLimitGuard } from 'zoltraak'
const limit = createRateLimitGuard({ maxRequests: 100, windowMs: 60_000 })
app.get('/api/search', handler, [limit])Performance variants
// Fast — skips Context creation
app.getFast('/health', () => new Response('OK'))
// Static — zero allocation, pre-compiled response
const PONG = new Response('pong')
app.getStatic('/ping', PONG)Configuration
const app = new Zoltraak({
port: 3000,
hostname: 'localhost',
security: {
headers: true,
bodyLimit: 1_048_576, // 1 MB
timeout: 30_000,
rateLimit: { maxRequests: 200, windowMs: 60_000 },
},
fastPath: {
enabled: true,
autoDetectStatic: true,
skipContextForSimple: true,
poolResponses: true,
},
})Lifecycle
app.onStart(async () => { await db.connect() })
app.onStop(async () => { await db.disconnect() })
await app.shutdown({ timeout: 10_000 }) // gracefulScripts
| Command | Action |
|---|---|
| bun run dev | Run examples/basic.ts with hot reload |
| bun test | Run test suite (154 tests) |
| bun run build | Compile TypeScript → dist/ |
| bun run typecheck | Type-check without building |
| bun run bench | Run benchmarks |
| bun run release | Build + publish to npm |
Links
MIT — dazcalifornia
