An opinionated framework for Hono on Cloudflare Workers, distributed under the
@katajs/*scope on npm.
Kata is a thin wiring layer that gives you modules, request-scoped DI, typed validation, transactions, queue producers and consumers, and a live devtools graph on top of tools you already know. It doesn't replace Hono, Zod, or Drizzle — it composes them into one shape so every project starts the same way.
pnpm create katajs my-app
cd my-app
pnpm install
pnpm devThat gives you a Worker with one example posts module, Drizzle + Hyperdrive wired up, validation, error mapping, and a /health route. Open http://localhost:8787/health.
Add flags to scaffold richer shapes:
pnpm create katajs my-app --auth # Better Auth integration
pnpm create katajs my-monorepo --monorepo # apps/* + packages/* layout
pnpm create katajs my-monorepo --monorepo --auth # both
pnpm create katajs my-monorepo --monorepo --worker # also: sibling queue WorkerOnce you're in a project, the katajs CLI ships mutators that keep wiring in sync as you grow:
katajs add module billing # new module + Registry + app.ts + graph
katajs add service stripe --in billing
katajs add route POST /webhook --in billing
katajs add queue invoices # consumer module + producer manifestA module declares what services it owns (provides), what services it needs from elsewhere (requires), and its routes:
// src/modules/posts/index.ts
import { defineModule } from '@katajs/core';
import { makePostService } from './posts.service';
import { makePostRepository } from './posts.repository';
import { postsRoutes } from './posts.routes';
export const postsModule = defineModule({
name: 'posts',
provides: {
postRepository: (c) => makePostRepository(c.db),
postService: (c) => makePostService(c),
},
requires: ['auditService'] as const,
routes: postsRoutes,
prefix: '/posts',
});A route resolves whatever it needs from the per-request container:
// src/modules/posts/posts.routes.ts
import { Hono } from 'hono';
import { validate } from '@katajs/core';
import { CreatePostSchema } from './posts.schema';
export const postsRoutes = new Hono<AppEnv>()
.post('/', ...validate({ body: CreatePostSchema }), async (c) => {
const input = c.req.valid('json');
const service = c.var.resolve('postService');
const post = await service.create(input);
return c.json({ post }, 201);
});createApp boots the Hono app, validates the module graph, and mounts everything:
// src/app.ts
const { app, queue } = createApp({
bindings: {} as Bindings,
db: drizzleAdapter({ schema }),
modules: [eventsModule, auditModule, postsModule],
queues: {
auditEvents: { binding: 'AUDIT_QUEUE', schema: AuditEventSchema },
},
routes: (base) => base
.get('/health', (c) => c.json({ ok: true }))
.route(postsModule.prefix, postsModule.routes),
});
export default { fetch: app.fetch, queue };
export type AppType = typeof app; // for Hono RPCThat's most of the framework. The rest is conventions for how modules organize files, how transactions thread through services, and how queue consumers attach to modules.
npx katajs-devtoolsOpens a live React UI showing the module graph, dependency edges, route table, and queue producer/consumer pairs. Watches src/modules/** and hot-reloads on every save.
For static snapshots, pnpm graph produces a self-contained graph.html you can drop into a PR or design doc.
Hono is excellent. Zod is excellent. Drizzle is excellent. But every Hono+Workers project ends up reinventing the same shapes — request context, error mapping, transaction scoping, where validation goes, how services share dependencies, where the schema lives. Kata is the version of those decisions I want to stop making twice.
It's not trying to be NestJS for Workers. It is trying to be the smallest opinionated layer that makes a Hono+Cloudflare+Drizzle project boring to start.
What it's for:
- Backend Workers with Postgres (Hyperdrive), validation at boundaries, RPC-typed clients.
- Projects that benefit from explicit module boundaries with explicit dependencies.
- Apps that consume or produce Cloudflare Queues and want both halves typed end-to-end.
- People who want one less decision to make at the start of every project.
What it's not for:
- Static sites or pure SSR — use Astro / TanStack Start.
- Multi-runtime backends — Kata is Cloudflare-first.
- Generic Hono utility kits — we ship one shape, not a buffet.
| Layer | Tool | Where Kata adds value |
|---|---|---|
| HTTP server | Hono | Plumbed into module routes, RPC types preserved end-to-end |
| Validation | Zod + @hono/zod-validator | validate() wrapper that throws typed errors |
| ORM | Drizzle ORM | withTransaction wires tx-bound services through the container |
| Postgres pool | Hyperdrive + pg | Adapter is one config call |
| Async messaging | Cloudflare Queues | Consumer-on-module, producer-on-app, schemas shared by import |
| Bundler | tsup | n/a |
| Tests | Vitest | n/a |
The runtime itself is ~1500 lines. Most of it is the module/container contract; everything else is delegated.
| Package | What it is |
|---|---|
@katajs/core |
The runtime — defineModule, createApp, container, errors, validation, queue types, inspectModules. |
@katajs/drizzle |
Drizzle + Hyperdrive Postgres adapter with withTransaction. |
@katajs/cli |
The project-mutator CLI — `katajs add module |
@katajs/devtools |
Live interactive module graph (npx katajs-devtools). |
create-katajs |
The scaffolder (pnpm create katajs). |
The full doc set lives in apps/docs (Fumadocs on TanStack Start, deployable to Cloudflare Workers). The same source markdown also lives at docs/concepts/ for direct browsing on GitHub:
- Intro — what Kata is and isn't
- Modules — the unit of organization
- Container — request-scoped DI, lazy resolution
- Registry — module augmentation, strict-resolve typing
- Routes — how routes mount, how RPC works
- Validation — Zod +
validate()at boundaries - Errors —
AppError,errorMapper, response shape - Transactions —
withTransaction, repository pattern - Testing — unit tests + integration tests
- Queues — consumer-on-module, producer-on-app
- Devtools —
inspectModules(), the graph script, the live UI - Architecture — when to split modules, when to graduate to monorepo
packages/
core/ — @katajs/core
drizzle/ — @katajs/drizzle
cli/ — create-katajs (project scaffolder)
katajs-cli/ — @katajs/cli (project mutator)
devtools/ — @katajs/devtools
apps/
docs/ — Fumadocs docs site (deployable to Cloudflare Workers)
examples/
basic/ — minimal scaffold
showcase/ — every feature, with real-Postgres tests
docs/
concepts/ — explainer docs (canonical source for apps/docs/content)
Build and test the whole workspace:
pnpm install
pnpm typecheck
pnpm test
pnpm buildThe showcase example tests against a real Postgres instance. See examples/showcase/README.md if present, otherwise: you need a local Postgres on 5432, db katajs_showcase_test. Run pnpm --filter showcase-example db:setup then pnpm --filter showcase-example test:pg.
v0.1 — runtime, scaffolder, CLI mutators, queues, devtools (static + live), and the docs site are all in place. See TODO.md for what's planned beyond v0.1 (multi-Worker devtools, ecosystem adapters, source-link integration).
MIT © Yaseer A. Okino
