@forinda/kickjs
v6.5.1
Published
A production-grade, decorator-driven Node.js framework for TypeScript — runs on Express, Fastify, or h3 (swap the engine in one line).
Downloads
2,243
Maintainers
Readme
@forinda/kickjs
Decorator-driven Node.js framework for TypeScript — runs on Express, Fastify, or h3 (swap the engine in one line). Custom DI container, factory-first module system, code generators, Zod-native validation, end-to-end type safety via typegen, and Vite HMR for sub-200ms dev reloads.
Install
# Scaffold a new project (recommended) — gets you a complete layout in seconds
npx @forinda/kickjs-cli new my-api && cd my-api && pnpm dev
# Or add to an existing project (Express is the zero-config default engine)
pnpm add @forinda/kickjs express reflect-metadata zod
pnpm add -D @forinda/kickjs-cli @forinda/kickjs-vite
# Prefer another engine? Install its peer and swap one line (see HTTP Runtimes):
# pnpm add fastify @fastify/middie → runtime: fastifyRuntime()
# pnpm add h3 → runtime: h3Runtime()Getting Started
The CLI scaffolds a complete layout. The files below show the canonical shapes — copy them into an existing project if you're not using kick new.
1. Service (@Service + DI)
// src/modules/users/user.service.ts
import { Service } from '@forinda/kickjs'
@Service()
export class UserService {
list() {
return [{ id: '1', name: 'Alice' }]
}
create(input: { name: string; email: string }) {
return { id: '2', ...input }
}
}2. Controller (@Controller, decorators, typed ctx)
// src/modules/users/user.controller.ts
import { Controller, Get, Post, Autowired, type Ctx } from '@forinda/kickjs'
import { z } from 'zod'
import { UserService } from './user.service'
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
@Controller()
export class UserController {
@Autowired() private readonly users!: UserService
@Get('/')
list(ctx: Ctx<KickRoutes.UserController['list']>) {
ctx.json(this.users.list())
}
@Post('/', { body: createUserSchema, name: 'CreateUser' })
create(ctx: Ctx<KickRoutes.UserController['create']>) {
// ctx.body is validated + typed from the Zod schema
ctx.created(this.users.create(ctx.body))
}
}Ctx<KickRoutes.UserController['list']> is generated by kick typegen (auto-runs on kick dev) — ctx.params, ctx.body, ctx.query all narrow to the Zod-derived shape as you save.
3. Module (defineModule() factory)
// src/modules/users/user.module.ts
import { defineModule } from '@forinda/kickjs'
import { UserController } from './user.controller'
export const UserModule = defineModule({
name: 'UserModule',
build: () => ({
routes() {
return { path: '/users', controller: UserController }
},
}),
})The framework derives the router from controller — never import buildRoutes directly. path is the single source of truth for the mount prefix; @Controller() takes no path argument.
4. Module registry (defineModules() fluent chain)
// src/modules/index.ts
import { defineModules } from '@forinda/kickjs'
import { UserModule } from './users/user.module'
// Factories are called at the registration site — `UserModule()` produces
// the AppModule instance bootstrap registers. Each `.mount(...)` call adds
// one module; chain as many as you need.
export const modules = defineModules().mount(UserModule())5. Bootstrap entry
// src/index.ts
import 'reflect-metadata'
import './config' // side-effect — registers env schema BEFORE bootstrap runs
import { bootstrap } from '@forinda/kickjs'
import { modules } from './modules'
// Always export the app. The Vite plugin reads this symbol to wire HMR;
// skipping `export` works in production but degrades dev mode to full restarts.
export const app = await bootstrap({ modules })That's it — http://localhost:3000/api/v1/users returns Alice; POST /api/v1/users with { name, email } validates against the Zod schema.
HTTP Runtimes — Express, Fastify, or h3
The HTTP engine is pluggable. Controllers, DI, decorators, and ctx are engine-neutral; pick the engine in bootstrap() (Express is the default — no import needed):
import { bootstrap } from '@forinda/kickjs'
import { fastifyRuntime } from '@forinda/kickjs/fastify' // or @forinda/kickjs/h3 → h3Runtime
export const app = await bootstrap({ modules, runtime: fastifyRuntime() })kick new --runtime express|fastify|h3 scaffolds the right peers. File uploads (@FileUpload → ctx.file / ctx.files) and the rest of the surface work the same on all three. See HTTP Runtimes.
Two web-standard entries sit alongside them (h3 v2 engine — additive, the v1 runtime is untouched):
import { h3WebRuntime } from '@forinda/kickjs/h3-web' // bootstrap() on node, WHATWG pipeline
import { createWebApp } from '@forinda/kickjs/web' // fetch(Request) → Response — Workers / Bun / Deno
import * as h3 from 'h3' // v2 — passed in: edge bundlers have no createRequire
import { modules } from './modules'
const app = createWebApp({ h3, modules })
export default { fetch: (req: Request) => app.fetch(req) }See Edge Deployment.
Project Layout
kick new produces this convention; everything except the bootstrap entry is configurable via kick.config.ts:
src/
index.ts # bootstrap entry — exports `app`
config/index.ts # env schema via defineEnv() + loadEnv()
modules/
index.ts # defineModules() chain
<name>/<name>.module.ts # one module per feature
<name>/<name>.controller.ts
<name>/<name>.service.ts
middleware/ # optional — `@Middleware()` factories + global stack
adapters/ # optional — `defineAdapter()` integrations
plugins/ # optional — `definePlugin()` bundles
.agents/ # AI agent context — AGENTS.md + skills/<slug>/SKILL.md
CLAUDE.md # Claude Code root (thin pointer to .agents/)
kick.config.ts # CLI configuration (optional)
vite.config.ts # Vite + KickJS HMRCore Concepts
- Pluggable HTTP runtimes — one
HttpRuntimeseam over Express (default), Fastify, or h3. Swap the engine in one line; everything above stays the same. See HTTP Runtimes. - Return-value handlers + typed client —
returnthe payload (reply(201, body)for other statuses) and the response type flows throughkick typegenintoKickRoutes.Api, consumed by@forinda/kickjs-clienton the frontend. Declare{ response: schema }on a route and the same contract feeds the OpenAPI success response. See Controllers, Typed Client. - First-class database —
kick/db: code-first schema, fully typed queries, and migrations for PostgreSQL / SQLite / MySQL.kick add db(orpg/sqlite/mysql). See Database. - Custom DI container — three scopes (singleton / transient / request), slash-delimited tokens (
createToken<T>('app/users/repository')), constructor + property injection. No external DI dep. See Dependency Injection. - Factory-first —
defineAdapter(),definePlugin(),defineModule(),defineHttpContextDecorator(). No class hierarchies to inherit from. See Adapters, Plugins. - Context Contributors — replace single-purpose middleware that only sets
ctxvalues. TypeddependsOn(typos are TS errors, not boot-time crashes), topo-sorted at startup, runs across HTTP / WS / queue / cron. See Context Decorators. - Env wiring —
defineEnv()+loadEnv()insrc/config/index.ts. Theimport './config'side-effect import at the top ofsrc/index.tsMUST happen before any@Value()injection resolves. See Configuration. - Asset Manager — typed
assets.<ns>.<key>()accessor, configured viaassetMapinkick.config.ts. Drops the__dirnamearithmetic that production builds get wrong. See Asset Manager. - Reactive primitives —
ref()/computed()/reactive()/watch()Vue-style;refandcomputedauto-unwrap onJSON.stringifyso they drop straight intointrospect()snapshots. - DevTools —
/_debugbrowser panel with topology, container, contributors, metrics. Adapters expose state viaintrospect(): IntrospectionSnapshot(type lives in@forinda/kickjs— no extra import needed). See DevTools.
Common Add-Ons
kick add db # kick/db — typed, code-first DB (+ pg / sqlite / mysql)
kick add upload # file-upload driver for your runtime (multer / @fastify/multipart / native)
kick add swagger # OpenAPI docs from decorators + Zod schemas
kick add devtools # /_debug dashboard
kick add ws # WebSocket with @WsController, rooms, heartbeat
kick add queue # BullMQ / RabbitMQ / Kafka jobs
kick add testing # createTestApp + createTestModule helpers
kick add --list # full live catalog
kick doctor # pre-flight checks: engine peers, upload driver, env wiringDocumentation
kickjs.app — full guide, API reference, BYO recipes (auth, GraphQL, OTel, cron, mailer, multi-tenancy, notifications), example apps.
Start here:
- Getting Started
- Modules · Controllers · Middleware
- Context Decorators · Adapters · Plugins
- BYO Recipes — auth, GraphQL, OpenTelemetry, cron, mailer, multi-tenancy, notifications
License
MIT
