Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

forinda/kick-js

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,642 Commits
1,642 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KickJS

A production-grade, decorator-driven Node.js framework for TypeScript — runs on Express, Fastify, or h3, swap the engine in one line.

npm version npm weekly downloads CI status TypeScript types license node version GitHub stars

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

Or a fullstack workspace — KickJS API + Vite/React frontend, typed end to end:

npx @forinda/kickjs-cli new my-app --template fullstack
cd my-app && pnpm dev   # server + web in parallel; rename a server field and the frontend stops compiling

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 / response per route
KickRoutes.Api // flat 'METHOD /path' map — powers the typed client
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

Return-value handlers + typed clientreturn the payload and the response type flows from the handler through kick typegen into @forinda/kickjs-client: the frontend calls api.get('/tasks/:id', …) with the server's actual types. Declare { response: schema } on a route and the same contract feeds the OpenAPI success response — docs and types can't drift.

@Get('/:id')
async get(ctx: RequestContext) {
  return this.tasks.find(ctx.params.id) // → 200 json; response type inferred end to end
}

Edge-ready web standards — the same app runs as a fetch(Request) → Response handler on Cloudflare Workers, Bun, and Deno via @forinda/kickjs/web (h3 v2 engine); a bundle-purity test keeps the entry free of node-only imports.

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, pluggable HTTP runtimes (Express / Fastify / h3), 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

One optional package lives on the frontend side instead: @forinda/kickjs-client — the zero-dependency typed fetch client (pnpm add @forinda/kickjs-client in your web app; see the typed client guide).

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 / Fastify / h3 Pluggable runtime — pick the engine at bootstrap
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 console (pluggable) Zero-dep default; swap for Pino / Winston via Logger.setProvider()
Monorepo pnpm + Turborepo Efficient deps, build caching

Runtime Compatibility

Feature Node 20+ Bun Deno Cloudflare Workers
Production (node runtimes) Yes Yes
Production (web fetch entry) Yes Yes Yes Yes¹
Dev Mode (Vite HMR) Yes No No
Tests (Vitest) Yes Partial No
CLI (kick) Yes Experimental No

Node 20 is the minimum supported version (LTS with native ESM).

¹ Workers need compatibility_flags = ["nodejs_compat"] (AsyncLocalStorage). The web fetch entry is @forinda/kickjs/webcreateWebApp({ h3, modules }) on the h3 v2 engine; Bun/Deno smoke tests run in CI.

Documentation

kickjs.app

New here? The Samples guide is a one-page, copy-pasteable tour of the recommended patterns — controllers, DI, modules, config, context decorators, error handling, adapters, and extending the CLI — each linking into the full guide. It doubles as the mental model.

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

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.