Project Structure
Where your declarations, your config, and your clients live.
Kizuna has no opinion about your file layout. A single file works. The two fixed points are kizuna.config.ts at the root of your app, which is where the CLI looks, and the k you declare everything against.
The layout below is the one we use ourselves. It goes from a handful of routes to a few hundred without anything moving.
my-api/
├── kizuna.config.ts # defineConfig, default-exported
├── kizuna.types.ts # written by kizuna generate
└── src/
├── k.ts # new Kizuna<Config>(), exports k
├── tags.ts # k.tags
├── identities.ts # k.identity.bearer(...).guard(...)
├── request-context.ts # k.requestContext(...).handler(...)
├── permissions.ts # Kizuna.permissions
├── roles.ts # Kizuna.roles
├── jobs.ts # k.jobs
├── routes/
│ ├── users.ts # k.routes('users', { ... })
│ ├── events.ts
│ ├── health.ts
│ └── index.ts # re-exports the groups
└── index.ts # kizuna.api.mount(app)Each route carries the handler that answers it. The same holds for the rest: a guard sits on its identity, a resolver sits on its request context value, and a job's handler sits on the job.
k.tsconstructs the surface once, typed by theConfiginkizuna.types.ts. Everything else importskfrom here.routes/holds one file per group, each callingk.routes, with anindex.tsre-exporting them so the config has one import to make. See splitting routes across files.identities.tsnames who can call the API, and each identity'sguardis what authenticates them. See Authentication.roles.tsnames who a caller can be, and each route'sauthsays who may call it. See Access Control.kizuna.config.tsis where the adapter, the routes, the identities, the jobs and the plugins come together. A browser imports the generated client written from it.
The @/ alias
Examples throughout these docs import from @/k and @/routes. That is @/ pointing at src/, which create-next-app sets up for you. Anywhere else, declare it yourself:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}