Skip to content

forinda/kick-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,171 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KickJS

A production-grade, decorator-driven Node.js framework built on Express 5 and TypeScript.

npm version license node version

NestJS ergonomics without the complexity — decorators, DI, module system, code generators, and end-to-end type safety, powered by Zod and Vite.

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 { defineModule } from '@forinda/kickjs'
import { HelloController } from './hello.controller'

export const HelloModule = defineModule({
  name: 'HelloModule',
  build: () => ({
    routes() {
      return { path: '/hello', controller: HelloController }
    },
  }),
})
// src/modules/index.ts
import { defineModules } from '@forinda/kickjs'
import { HelloModule } from './hello/hello.module'

export const modules = defineModules().mount(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 by kick typegen (auto-runs on kick dev), giving fully typed ctx.params, ctx.body, and ctx.query. Env keys typed via KickEnv after running typegen too.

Highlights

Factory-first extensibility — the full extension surface. No class hierarchies to inherit from.

defineAdapter() defineModule()
definePlugin() defineHttpContextDecorator()

Custom DI container — constructor and property injection, three scopes (singleton / transient / request), zero external dependency. Slash-delimited tokens carry intent and scope from the type definition straight through to error messages.

const REPO = createToken<UserRepo>('app/users/repository')
container.register(REPO, PrismaUserRepo)

Typed Context ContributorsdefineHttpContextDecorator() populates ctx.set('key', value) once per request. dependsOn is typed against keyof ContextMeta so typos are TS errors, not boot-time MissingContributorError. Same registration runs across HTTP / WS / queue / cron.

End-to-end type safety via typegenkick typegen (auto on kick dev) emits augmentations from source scan. ctx.params/body/query, @Inject literals, and asset paths all narrow as you save.

KickRoutes // ctx.params / body / query per route
KickJsPluginRegistry // @Inject literals
KickAssets // typed asset paths
KickEnv // ConfigService.get keys

Decorator-driven:

@Controller @Get @Post @Put @Delete @Patch
@Service @Autowired @Middleware
@Cacheable @Cron @Asset

DDD generators — full hook surface emitted so you delete what you don't need.

kick g module users  # complete module scaffold
kick g adapter       # full hook surface
kick g plugin        # full plugin shape

kick g agents — regenerates CLAUDE.md at the project root and .agents/AGENTS.md / .agents/GEMINI.md / .agents/COPILOT.md + per-skill .agents/skills/<slug>/SKILL.md. One CLI command keeps every AI coding agent in sync with the latest framework conventions.

Cooperative shutdown — observability SDKs (OpenTelemetry, Sentry) own SIGTERM without racing the framework. Promise.allSettled for adapter shutdown so one slow flush can't block siblings.

bootstrap({ processHooks: 'errors-only' })

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.

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

DevTools dashboard/_debug browser panel with topology, container, routes, metrics; adapter authors expose state via introspect() (type lives in @forinda/kickjs directly — no extra import needed) + devtoolsTabs() from @forinda/kickjs-devtools-kit.

Extensible CLI — custom commands in kick.config.ts, plugin generators registered as real Commander subcommands so they appear in kick g --help, jiti-powered TS config loading, walk-up project-root resolution.

Ecosystem

KickJS deliberately ships a small, stable core. The extension surface — defineAdapter(), definePlugin(), defineHttpContextDecorator(), plus getRequestValue and processHooks from @forinda/kickjs — is the same one the framework itself uses, so adopter-built integrations stay first-class: typed DI, lifecycle hooks, the contributor pipeline, and DevTools introspect() all work without any framework changes.

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, jiti-powered TS config loading, walk-up project root

Optional packages

Everything else — swagger, the db family, queue, ws, devtools, drizzle, prisma — installs on demand. The catalog moves over time, so the live list lives next to the CLI rather than this README:

kick add --list           # current optional catalog
kick add swagger drizzle  # install several at once

Browse packages/ in this repo for the full source layout.

Example Apps

Runnable reference apps live in forinda/kickjs-examples-archive — Drizzle / Prisma / Mongoose / @forinda/kickjs-db task apps, multi-tenant patterns, and a minimal starter. Open the archive's README for the current catalog.

git clone https://github.com/forinda/kickjs-examples-archive

The fastest way to start a real project is still kick new <name>kick.config.ts, tsconfig, vite.config, modules, and env wiring scaffolded for you. The archive is for reading reference patterns; the CLI is for starting your own.

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
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 users              # 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 job / dto / guard / middleware / test  # one-file scaffolds

# AI agent docs (regenerate from upstream templates after framework upgrades)
kick g agents                        # CLAUDE.md (root) + .agents/{AGENTS,GEMINI,COPILOT}.md + .agents/skills/*/SKILL.md
kick g agents --only skills -f       # Just the per-skill SKILL.md files
kick g agents --only gemini -f       # Just .agents/GEMINI.md

# Package management
kick add swagger drizzle             # Install KickJS packages with peer deps
kick add --list                      # Show all available packages

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

Bun: core DI and decorators work; full HTTP pipeline is experimental.

Deno: blocked by reflect-metadata and pino dependencies. Use Logger.setProvider() for core-only usage.

Documentation

forinda.github.io/kick-js

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

Packages

 
 
 

Contributors

Languages