Skip to content

ookino/katajs

Repository files navigation

Kata logo

Kata

The opinionated Hono framework for Cloudflare's full stack.

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.


Quickstart

pnpm create katajs my-app
cd my-app
pnpm install
pnpm dev

That 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 Worker

Once 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 manifest

What your code looks like

A 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 RPC

That'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.


Devtools

npx katajs-devtools

Opens 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.

Kata devtools

For static snapshots, pnpm graph produces a self-contained graph.html you can drop into a PR or design doc.


Why does this exist?

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.

What it builds on (and what it adds)

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.


Packages

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).

Docs

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
  • ErrorsAppError, errorMapper, response shape
  • TransactionswithTransaction, repository pattern
  • Testing — unit tests + integration tests
  • Queues — consumer-on-module, producer-on-app
  • DevtoolsinspectModules(), the graph script, the live UI
  • Architecture — when to split modules, when to graduate to monorepo

Repo layout

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 build

The 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.


Status

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).


License

MIT © Yaseer A. Okino

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages