The art of perfect balance
Lightweight · Type-Safe · Web Standards · Bun
Most backend bugs come from one assumption:
“this will be there when I need it”
Middleware makes that assumption easy — and wrong.
- You forget to register it
- You register it in the wrong order
- You access something that was never set
Everything compiles. Everything deploys. Then it breaks.
Tomoe removes that assumption entirely.
If a route depends on something, it must be declared. If it’s declared, it must be satisfied. If it’s not satisfied, your app doesn’t start.
bun init
bun add tomoejsimport { Tomoe } from "tomoejs"
const app = new Tomoe()
app.get("/", (c) => c.text("Hello Tomoe"))
export default appbun run index.tsInstead of hoping something exists:
app.use(authMiddleware)
app.get("/me", (c) => c.json(c.get("user"))) // maybeYou define a contract:
import { relic, token, unite, err, Unauthorized } from "tomoejs"
const UserCtx = token<{ id: string }>("user")
const authRelic = relic(UserCtx, async (ctx) => {
const user = await db.verify(ctx.req.headers.get("authorization"))
if (!user) return err(Unauthorized)
return user
})
app.get("/me", authRelic, (ctx) => {
return ctx.json(ctx.relic(UserCtx)) // guaranteed
})No guessing. No undefined. No order bugs.
Middleware → Relics → Handler
- Middleware controls flow
- Relics define required state
- Handlers stay pure
Relics are validated at startup.
- Typed routing and params
- Native Web API request/response
- Composable middleware
- Contract-based data flow (Relics)
- Startup-time validation
A tomoe (巴) represents balance — the same balance between:
- performance
- type safety
- developer experience
MIT License · Built with 🌸
