@forinda/kickjs-http
Express-based HTTP layer with declarative middleware pipeline, request context, routing, and query string parsing.
Application
Main application class that wires Express, the DI container, modules, adapters, and middleware.
typescript
class Application {
constructor(options: ApplicationOptions)
setup(): void
start(): void
rebuild(): void
shutdown(): Promise<void>
getExpressApp(): Express
getHttpServer(): http.Server | null
}
interface ApplicationOptions {
modules: AppModuleClass[]
adapters?: AppAdapter[]
port?: number
apiPrefix?: string // default: '/api'
defaultVersion?: number // default: 1
middleware?: MiddlewareEntry[]
trustProxy?: boolean | number | string | ((ip: string, hopIndex: number) => boolean)
jsonLimit?: string | number
}
type MiddlewareEntry =
| RequestHandler
| { path: string; handler: RequestHandler }bootstrap
Zero-boilerplate entry point. Handles Vite HMR, graceful shutdown, and global error handlers.
typescript
function bootstrap(options: ApplicationOptions): voidRequestContext
Unified request/response abstraction passed to every controller method.
typescript
class RequestContext<TBody = any, TParams = any, TQuery = any> {
readonly req: Request
readonly res: Response
readonly next: NextFunction
get body(): TBody
get params(): TParams
get query(): TQuery
get headers(): IncomingHttpHeaders
get requestId(): string | undefined
get file(): any
get files(): any[] | undefined
qs(fieldConfig?: QueryFieldConfig): ParsedQuery
get<T = any>(key: string): T | undefined
set(key: string, value: any): void
// Response helpers
json(data: any, status?: number): Response
created(data: any): Response
noContent(): Response
notFound(message?: string): Response
badRequest(message: string): Response
html(content: string, status?: number): Response
download(buffer: Buffer, filename: string, contentType?: string): Response
// Template rendering (requires ViewAdapter)
render(template: string, data?: Record<string, any>): void
// Pagination — parses query, fetches data, returns paginated JSON
paginate<T>(
fetcher: (parsed: ParsedQuery) => Promise<{ data: T[]; total: number }>,
fieldConfig?: QueryFieldConfig,
): Promise<Response>
// Server-Sent Events
sse(): {
send(data: any, event?: string, id?: string): void
comment(text: string): void
onClose(fn: () => void): void
close(): void
}
}Router Builder
typescript
function buildRoutes(controllerClass: any): Router
function getControllerPath(controllerClass: any): string- buildRoutes -- Builds an Express Router from a decorated controller class, resolving it from the DI container.
- getControllerPath -- Returns the path prefix set by
@Controller('/path').
Middleware
requestId
Generates or propagates a unique x-request-id header.
typescript
function requestId(): RequestHandler
const REQUEST_ID_HEADER = 'x-request-id'validate
Validates req.body, req.query, and req.params against schemas with .safeParse().
typescript
function validate(schema: { body?: any; query?: any; params?: any }): RequestHandlererrorHandler / notFoundHandler
typescript
function errorHandler(): ErrorRequestHandler
function notFoundHandler(): RequestHandlercsrf
Double-submit cookie CSRF protection.
typescript
function csrf(options?: CsrfOptions): RequestHandler
interface CsrfOptions {
cookie?: string // default: '_csrf'
header?: string // default: 'x-csrf-token'
methods?: string[] // default: ['POST','PUT','PATCH','DELETE']
ignorePaths?: string[]
tokenLength?: number // default: 32
cookieOptions?: { httpOnly?: boolean; sameSite?: 'strict'|'lax'|'none'; secure?: boolean; path?: string }
}upload
File upload middleware built on multer.
typescript
const upload: {
single(fieldName: string, options?: UploadOptions): RequestHandler
array(fieldName: string, maxCount?: number, options?: UploadOptions): RequestHandler
none(options?: UploadOptions): RequestHandler
}
function cleanupFiles(): RequestHandler
interface UploadOptions {
maxSize?: number // default: 5MB
allowedTypes?: string[]
storage?: MulterOptions['storage']
dest?: string
}Query String Parsing
ORM-agnostic query string parsing for filters, sorting, pagination, and search.
typescript
function parseQuery(query: Record<string, any>, fieldConfig?: QueryFieldConfig): ParsedQuery
function parseFilters(filterParam: string | string[] | undefined, allowedFields?: string[]): FilterItem[]
function parseSort(sortParam: string | string[] | undefined, allowedFields?: string[]): SortItem[]
function parsePagination(params: { page?: string | number; limit?: string | number }): PaginationParams
function parseSearchQuery(q: string | undefined): string
function buildQueryParams(parsed: Partial<ParsedQuery>): Record<string, string | string[] | number>Query Types
typescript
type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'in' | 'contains' | 'starts' | 'ends'
interface FilterItem { field: string; operator: FilterOperator; value: string }
interface SortItem { field: string; direction: 'asc' | 'desc' }
interface PaginationParams { page: number; limit: number; offset: number }
interface ParsedQuery { filters: FilterItem[]; sort: SortItem[]; pagination: PaginationParams; search: string }
interface QueryFieldConfig { filterable?: string[]; sortable?: string[]; searchable?: string[] }
interface QueryBuilderAdapter<TResult = any, TConfig = any> {
readonly name: string
build(parsed: ParsedQuery, config: TConfig): TResult
}