Kickjs
Model Context Protocol server adapter for KickJS β expose @Controller endpoints as MCP tools
Ask AI about Kickjs
Powered by Claude Β· Grounded in docs
I know everything about Kickjs. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
A production-grade, decorator-driven Node.js framework built on Express 5 and TypeScript.
NestJS ergonomics without the complexity β decorators, DI, module system, code generators, and end-to-end type safety, powered by Zod and Vite.
Heads-up β v4.2.0 just shipped. Six wrappers (
graphql,otel,cron,mailer,multi-tenant,notifications) are now flagged for removal in v5.0.0. Each is replaced by a 100-LOC BYO recipe that uses the framework's own primitives. See Migration v4 β v5 guide for the cutover; comparison.md for why this is the strategic shape.
Install
pnpm add @forinda/kickjs express reflect-metadata zod
pnpm add -D @forinda/kickjs-cli
Or scaffold a new project:
npx @forinda/kickjs-cli new my-api
cd my-api && pnpm dev
Hello World
A fresh kick new my-api scaffolds a complete project. Here are the files
that matter, exactly as the CLI generates them:
// src/modules/hello/hello.service.ts
import { Service } from '@forinda/kickjs'
@Service()
export class HelloService {
greet(name: string) {
return { message: `Hello ${name} from KickJS!`, timestamp: new Date().toISOString() }
}
healthCheck() {
return { status: 'ok', uptime: process.uptime() }
}
}
// src/modules/hello/hello.controller.ts
import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
import { HelloService } from './hello.service'
@Controller()
export class HelloController {
@Autowired() private readonly helloService!: HelloService
@Get('/')
index(ctx: Ctx<KickRoutes.HelloController['index']>) {
ctx.json(this.helloService.greet('World'))
}
@Get('/health')
health(ctx: Ctx<KickRoutes.HelloController['health']>) {
ctx.json(this.helloService.healthCheck())
}
}
// src/modules/hello/hello.module.ts
import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'
import { HelloController } from './hello.controller'
export class HelloModule implements AppModule {
routes(): ModuleRoutes {
return {
path: '/hello',
router: buildRoutes(HelloController),
controller: HelloController,
}
}
}
// src/modules/index.ts
import type { AppModuleClass } from '@forinda/kickjs'
import { HelloModule } from './hello/hello.module'
export const modules: AppModuleClass[] = [HelloModule]
// src/index.ts
import 'reflect-metadata'
import './config' // registers env schema before bootstrap
import { bootstrap } from '@forinda/kickjs'
import { modules } from './modules'
export const app = await bootstrap({ modules })
KickRoutes.HelloController['index']is generated bykick typegen(auto-runs onkick dev), giving fully typedctx.params,ctx.body, andctx.query. Env keys typed viaKickEnvafter running typegen too.
Highlights
- Factory-first extensibility β
defineAdapter()/definePlugin()/defineHttpContextDecorator()are the entire extension surface; no class hierarchies to inherit from - Custom DI container β constructor and property injection, slash-delimited tokens (
createToken<T>('app/users/repository')), three scopes (singleton / transient / request), no external dependency - Typed Context Contributors β
defineHttpContextDecorator()populatesctx.set('key', value)once per request;dependsOnis typed againstkeyof ContextMeta(typos are TS errors, not boot-timeMissingContributorError); same registration runs across HTTP / WS / queue / cron - End-to-end type safety via typegen β
kick typegen(auto onkick dev) emitsKickRoutes,KickJsPluginRegistry,KickAssets,KickEnvaugmentations from source scan;ctx.params/body/query,@Injectliterals, and asset paths all narrow as you save - Decorator-driven β
@Controller,@Get/@Post/@Put/@Delete/@Patch,@Service,@Autowired,@Middleware,@Roles,@Public,@Cacheable - Zod-native validation β schemas double as OpenAPI documentation
- Vite HMR β single-port dev server, zero-downtime hot reload, preserves DB/Redis/Socket connections, customizable HMR log
- DDD generators β
kick g module usersscaffolds 18 files in 2 seconds;kick g adapter/kick g pluginemit the full hook surface so you delete what you don't need kick g agentsβ regeneratesAGENTS.md/CLAUDE.md/kickjs-skills.mdin every project; one CLI command keeps every AI coding agent in sync with the latest framework conventions- Auto OpenAPI β Swagger UI and ReDoc from decorators + Zod schemas; pluggable schema parser + UI renderer for adopters who want corporate branding
- Built-in middleware β helmet, CORS, CSRF, rate limiting, file uploads, request logging, request scope (AsyncLocalStorage)
- Cooperative shutdown β
bootstrap({ processHooks: 'errors-only' })lets observability SDKs (OpenTelemetry, Sentry) own SIGTERM without racing the framework;Promise.allSettledfor adapter shutdown so one slow flush can't block siblings - DevTools dashboard β
/_debugbrowser panel with topology, container, routes, metrics; adapter authors expose state viaintrospect()+devtoolsTabs()from@forinda/kickjs-devtools-kit - Extensible CLI β custom commands in
kick.config.ts, plugin generators discovered fromnode_modules
Ecosystem
KickJS deliberately ships a small, stable core. The "right" extension surface is defineAdapter() / definePlugin() / defineHttpContextDecorator() plus getRequestValue / processHooks from @forinda/kickjs β adopters compose ecosystem-specific glue (GraphQL runtimes, OTel SDKs, mail providers, auth flows) on top of those primitives via short copy-paste recipes. Several wrappers are deprecated for v5 because the BYO recipe is shorter and ages better than a thin first-party wrapper around a fast-moving upstream.
@forinda/kickjs-authjoins the BYO list as of this release. The new parameterised context contributors (@LoadX({...})+.with()) cover every shape the package wrapped β JWT / API-key / OAuth / Session / Passport bridge β without coupling your auth surface to the framework's release cadence. See the BYO Auth recipe for the full migration guide and the Context Decorators guide for the underlying primitive.
Core packages
Three packages ship with every project β kick new always installs them, and kick add won't list them as optional. Together they're the framework runtime + the dev/build/scaffold loop:
| Package | Description |
|---|---|
@forinda/kickjs | Core framework β DI, decorators, Express 5, routing, middleware, contributors, request store, processHooks |
@forinda/kickjs-vite | Vite plugin β single-port HMR, typegen watcher, customizable HMR log |
@forinda/kickjs-cli | Scaffolding, DDD generators, custom commands, kick g agents |
Optional packages
Everything else β auth, swagger, db, queue, ws, devtools, etc. β installs on demand. The catalog moves over time (new packages land, deprecated ones move out), so the live list lives next to the CLI rather than this README:
kick add --list # current optional catalog
kick add auth swagger drizzle # install several at once
Browse packages/ in this repo for the full source layout.
Deprecated β going private in v5
These packages are still installable in v4.1.x and emit deprecation notices. The replacement for each is a copy-paste recipe under 100 lines that uses the supported core primitives directly. v5 removes them from the public registry; the in-tree source either disappears or stays as a private internal dep.
| Package | BYO replacement |
|---|---|
@forinda/kickjs-graphql | guide/graphql β wrap graphql-http / graphql-yoga / Apollo / Pothos via definePlugin |
@forinda/kickjs-otel | guide/otel β own the OpenTelemetry NodeSDK lifecycle; pair with bootstrap({ processHooks: 'errors-only' }) so the SDK's SIGTERM handler doesn't race the framework's |
@forinda/kickjs-cron | guide/cron β wrap croner via defineAdapter + framework metadata helpers |
@forinda/kickjs-mailer | guide/mailer β nodemailer / Resend / SES via definePlugin, plus a console-mailer asset-manager example |
@forinda/kickjs-multi-tenant | guide/multi-tenancy β defineHttpContextDecorator + Scope.REQUEST per-tenant DB factory |
@forinda/kickjs-notifications | guide/notifications β channel interface + definePlugin registration |
Why the cut: thin wrappers around fast-moving ecosystems were costing more in CI/build time than they were saving adopters. Each shipped wrapper made an opinionated choice (which OTel exporter, which mail provider, which GraphQL runtime) that adopters consistently swapped within weeks. The BYO recipes use the same primitives the framework itself exposes β so adopters keep typed DI, lifecycle hooks, the contributor pipeline, and DevTools introspect() β and stop paying for an extra layer they were going to bypass anyway. See comparison.md for the strategic positioning.
Example Apps
| Example | What it shows |
|---|---|
| minimal-api | Simplest possible app β bootstrap + one controller |
| task-drizzle-api | Full task management app β PostgreSQL + Drizzle, 14 DDD modules |
| task-prisma-api | Full task management app β PostgreSQL + Prisma 7 (driver adapters) |
| task-kickdb-api | KickJS-native ORM (M1) β PostgreSQL + @forinda/kickjs-db |
| task-mongoose-api | Full task management app β MongoDB + Mongoose |
| multi-tenant-drizzle-api | Multi-tenant pattern β PostgreSQL + Drizzle |
| multi-tenant-prisma-api | Multi-tenant pattern β PostgreSQL + Prisma |
| multi-tenant-mongoose-api | Multi-tenant pattern β MongoDB + Mongoose |
Adding an Example App
-
Build the CLI first (if not already built):
pnpm build -
Scaffold the example using the local CLI from the
examples/directory (pass all flags to avoid interactive prompts):cd examples node ../packages/cli/bin.js new my-example-api \ --template minimal --pm pnpm --repo inmemory --no-git --no-install --forceAvailable flags:
--template <type>βrest | ddd | cqrs | minimal--pm <manager>βpnpm | npm | yarn | bun--repo <type>βprisma | drizzle | inmemory | custom--no-gitβ skip git init (use repo root's git)--no-installβ skip install (runpnpm installfrom root instead)--forceβ overwrite existing directory without prompting
This generates
package.json,tsconfig.json,vite.config.ts,kick.config.ts,src/index.ts, and all boilerplate. -
The workspace already includes
examples/*inpnpm-workspace.yamlβ no changes needed there. -
Update the generated
package.json:- Rename to
@forinda/kickjs-example-<name> - Set
"private": true - Replace published
@forinda/kickjs*deps withworkspace:*references
- Rename to
-
Add a row to the Example Apps table in this README.
-
Run from the repo root to link workspace deps and verify:
pnpm install && pnpm build
CLI
# Project lifecycle
kick new my-api # Scaffold project (rest | ddd | cqrs | minimal)
kick dev # Vite HMR dev server (~200ms reload)
kick build && kick start # Production build + run
# Code generation
kick g module users # Full DDD module (18 files)
kick g module users --repo prisma # β¦with a Prisma repository
kick g module users --repo drizzle # β¦with a Drizzle repository
kick g scaffold post title:string body:text:optional # CRUD from field defs
kick g controller auth # Single @Controller class
kick g service payment # Single @Service class
kick g adapter websocket # AppAdapter β every hook stubbed + JSDoc
kick g plugin analytics # KickPlugin β every hook stubbed + JSDoc
kick g resolver / job / dto / guard / middleware / test # one-file scaffolds
# AI agent docs (regenerate from upstream templates after framework upgrades)
kick g agents # Refresh AGENTS.md / CLAUDE.md / kickjs-skills.md
kick g agents --only skills -f # Just the skills file
# Package management
kick add auth swagger drizzle # Install KickJS packages with peer deps
kick add --list # Show all available packages (deprecated ones are flagged)
# Introspection
kick info # System + framework version
kick inspect # Inspect a running KickJS app
kick tinker # Interactive REPL with full DI graph
Technical Decisions
| Area | Choice | Why |
|---|---|---|
| Runtime | Node.js 20+ | LTS with native ESM |
| HTTP | Express 5 | Mature, async middleware, wide ecosystem |
| Validation | Zod | Runtime + static types, doubles as OpenAPI schema |
| Build | Vite 8 | Unified toolchain β library builds, HMR, SSR |
| Test | Vitest 4 | ESM-native, fast, Vite-compatible |
| Logging | Pino | Fastest Node.js logger, structured JSON |
| Monorepo | pnpm + Turborepo | Efficient deps, build caching |
Runtime Compatibility
| Feature | Node 20+ | Node 22+ | Node 24+ | Bun | Deno |
|---|---|---|---|---|---|
| Production | Yes | Yes | Yes | Experimental | No |
| Dev Mode (HMR) | Yes | Yes | Yes | No | No |
| Tests (Vitest) | Yes | Yes | Yes* | Partial | No |
CLI (kick) | Yes | Yes | Yes | Experimental | No |
| Pure ESM Import | Yes | Yes | Yes | Yes | Yes |
Node 20 is the minimum supported version (LTS with native ESM).
Node 24: mailer tests need
server.deps.externalfor nodemailer CJS.Bun: core DI and decorators work; full HTTP pipeline is experimental.
Deno: blocked by
reflect-metadataandpinodependencies. UseLogger.setProvider()for core-only usage.
Documentation
Contributing
git clone https://github.com/forinda/kick-js.git
cd kick-js
pnpm install && pnpm build && pnpm test
See CONTRIBUTING.md for the full guide.
License
MIT β see LICENSE
