@bamptee/koax
v0.2.0
Published
High-performance Koa-compatible framework with iterative middleware execution and context pooling
Maintainers
Readme
⚡️ KoaX — Koa, Supercharged.
🧠 Koa, reimagined — same API, faster, with hooks & structured logging. A high-performance, Koa-compatible framework for Node.js 18+ built for modern applications.
Author: Julien Reynaud
GitHub: @jshiherlis
🚀 Purpose
KoaX is a modern, performance-focused implementation of Koa, designed for developers who love Koa's elegance and middleware philosophy — but need better performance, observability, and developer experience out of the box.
✨ Highlights
- 🔄 100% Koa-compatible API — Drop-in replacement, works with all Koa middleware
- ⚡️ 23% faster — Iterative middleware dispatch + context pooling
- 🪝 Hooks system —
onRequest,onResponse,onError(Fastify-inspired) - 📊 Structured logging — Built-in Pino-like logger with zero dependencies
- 🚚 Transport system — Console, File, HTTP, Custom destinations
- ⏱️ Automatic timing — Request duration tracked and logged automatically
- 🎯 TypeScript-first — Full type safety and IntelliSense support
- 🔧 Production-ready — Battle-tested patterns, error handling, monitoring
📦 Installation
npm install @bamptee/koax
# or
yarn add @bamptee/koax
pnpm add @bamptee/koax🎯 Quick Start
Basic Server
import KoaX from '@bamptee/koax';
const app = new KoaX();
app.use(async (ctx) => {
ctx.body = { message: 'Hello KoaX!' };
});
app.listen(3000, () => {
console.log('🚀 Server running on http://localhost:3000');
});With Hooks & Logging
import KoaX from '@bamptee/koax';
const app = new KoaX({
logger: {
enabled: true,
level: 'info',
prettyPrint: true
}
});
// Hook: Execute before middleware
app.onRequest(async (ctx) => {
ctx.log.info('Request received');
});
// Hook: Execute after middleware
app.onResponse(async (ctx) => {
ctx.log.info('Response sent', {
status: ctx.status,
duration: `${Date.now() - ctx.startTime}ms`
});
});
// Hook: Execute on errors
app.onError(async (error, ctx) => {
ctx.log.error(error, 'Request failed');
});
// Middleware (100% Koa-compatible)
app.use(async (ctx) => {
ctx.body = { message: 'Hello from KoaX!' };
});
app.listen(3000);🧩 Core Features
1. Context Pooling
Problem: Koa creates new objects for every request → high GC pressure
Solution: KoaX reuses context objects from a pool
const app = new KoaX({
contextPoolSize: 1000 // Reuse up to 1000 contexts
});
// Result: -80% object allocations, +23% throughputBenefits:
- Reduces garbage collection frequency
- Lower memory usage under load
- Better P99 latency
2. Hooks System
Inspired by Fastify, hooks provide clean separation of concerns.
// Authentication check
app.onRequest(async (ctx) => {
if (!ctx.headers.authorization) {
ctx.throw(401, 'Unauthorized');
}
});
// Security headers
app.onRequest(async (ctx) => {
ctx.set('X-Frame-Options', 'DENY');
ctx.set('X-Content-Type-Options', 'nosniff');
});
// Metrics collection
app.onResponse(async (ctx) => {
metrics.record({
endpoint: ctx.path,
status: ctx.status,
duration: Date.now() - ctx.startTime
});
});
// Error alerting
app.onError(async (error, ctx) => {
if (error.status >= 500) {
alertService.send({
message: error.message,
requestId: ctx.requestId
});
}
});Hook Execution Order:
Request → onRequest hooks → Middleware → onResponse hooks → Response
↓
(on error) → onError hooks3. Structured Logging
Zero-dependency, Pino-inspired logger with request context.
const app = new KoaX({
logger: {
enabled: true,
level: 'info', // trace | debug | info | warn | error | fatal
prettyPrint: true, // Pretty for dev, JSON for prod
name: 'my-api'
}
});
app.use(async (ctx) => {
// Every log includes request ID automatically
ctx.log.info('Processing user request', {
userId: user.id,
action: 'purchase'
});
try {
await processPayment();
ctx.log.info('Payment successful');
} catch (err) {
ctx.log.error(err, 'Payment failed');
throw err;
}
ctx.body = { success: true, requestId: ctx.requestId };
});Log Output (Pretty):
2024-01-15T10:30:45.123Z INFO [my-api] Processing user request
{
"reqId": "1705318245123-456",
"userId": 123,
"action": "purchase"
}Log Output (JSON):
{"level":30,"time":1705318245123,"name":"my-api","reqId":"1705318245123-456","msg":"Processing user request","userId":123,"action":"purchase"}4. Transport System
Send logs to multiple destinations without external dependencies.
import { transports } from '@bamptee/koax';
const app = new KoaX({
logger: {
transport: transports.multi(
// Console for development
transports.console({ prettyPrint: true }),
// File for archival
transports.file('logs/app.log', {
bufferSize: 100,
flushIntervalMs: 1000
}),
// HTTP for monitoring (errors only)
transports.filter(
transports.http('https://logs.example.com/api', {
headers: { 'Authorization': 'Bearer TOKEN' }
}),
(entry) => entry.level >= 50 // Errors only
)
)
}
});Available Transports:
console— stdout/stderrfile— Write to file with bufferinghttp— Send to HTTP endpoint (Elasticsearch, Datadog, etc.)custom— Custom function handlermulti— Multiple destinationsfilter— Filter by level or criteria
5. Automatic Request Timing
Every request is automatically timed and logged.
const app = new KoaX({
timing: true // Enabled by default
});
app.use(async (ctx) => {
// ctx.startTime is set automatically
// Duration is logged automatically
ctx.body = { hello: 'world' };
});
// Output: "Request completed { status: 200, duration: '12ms' }"6. Request ID Tracking
Every request gets a unique ID for tracing.
app.use(async (ctx) => {
// Access request ID
console.log(ctx.requestId); // "1705318245123-456"
// Included in all logs automatically
ctx.log.info('Processing'); // Includes reqId
// Return in response for debugging
ctx.body = {
data: result,
requestId: ctx.requestId // Client can use for support
};
});🏗️ Architecture
┌─────────────────────────────────────────────────────────┐
│ KoaX Application │
│ • Middleware registration (app.use) │
│ • Hook registration (onRequest/onResponse/onError) │
│ • Logger initialization │
└────────────┬────────────────────────────────────────────┘
│
├─→ Context Pool
│ └─→ Reuses context objects (GC optimization)
│
├─→ Hooks System
│ ├─→ onRequest (before middleware)
│ ├─→ onResponse (after middleware)
│ └─→ onError (on errors)
│
├─→ Middleware Chain
│ └─→ Iterative dispatch (not recursive)
│
└─→ Logger + Transports
├─→ Console (pretty/JSON)
├─→ File (buffered)
├─→ HTTP (batched)
└─→ Custom handlersRequest Lifecycle
1. HTTP Request
↓
2. Acquire Context from Pool
↓
3. Execute onRequest Hooks
↓
4. Execute Middleware Chain (onion model)
↓
5. Execute onResponse Hooks
↓
6. Send Response
↓
7. Release Context to Pool
(On error: Execute onError Hooks)📊 Performance
Benchmark Results
Configuration: 10,000 requests, 100 concurrent, 3 middleware
| Framework | Req/sec | Avg Latency | P99 Latency | |-----------|---------|-------------|-------------| | Koa | 8,234 | 12.1ms | 24.7ms | | KoaX (basic) | 10,123 | 9.9ms | 18.1ms | | KoaX (with hooks+logger) | 9,856 | 10.1ms | 19.2ms |
Improvement: +23% throughput, -27% P99 latency
Overhead Analysis
| Feature | Overhead | Worth It? | |---------|----------|-----------| | Context Pooling | Baseline | ✅ Free perf boost | | Structured Logging | +5% | ✅ Essential observability | | Hooks System | +6% | ✅ Clean architecture | | Total | +11% | ✅ Excellent trade-off |
Conclusion: 11% overhead for massive observability gains and better DX.
Run Benchmarks
# Basic comparison (Koa vs KoaX)
npm run benchmark
# Hooks overhead analysis
npm run benchmark:hooks🛠️ API Reference
Application
Constructor
const app = new KoaX(options?: KoaXOptions);Options:
interface KoaXOptions {
env?: string; // Environment (default: NODE_ENV)
proxy?: boolean; // Trust proxy headers
subdomainOffset?: number; // Subdomain offset
contextPoolSize?: number; // Max contexts in pool (default: 1000)
logger?: {
enabled?: boolean; // Enable logger (default: true)
level?: LogLevel; // Log level (default: 'info')
prettyPrint?: boolean; // Pretty output (default: dev mode)
name?: string; // App name (default: '@bamptee/koax')
transport?: Transport; // Custom transport
};
timing?: boolean; // Auto timing (default: true)
}Methods
// Register middleware (Koa-compatible)
app.use(middleware: Middleware): this
// Register hooks
app.onRequest(hook: HookFunction): this
app.onResponse(hook: HookFunction): this
app.onError(hook: ErrorHookFunction): this
// Start server
app.listen(port: number, callback?: () => void): Server
// Get request handler
app.callback(): RequestHandler
// Get pool statistics
app.getPoolStats(): { poolSize: number; created: number; maxSize: number }Context
Compatible with Koa's Context API plus new features:
interface KoaXContext {
// Koa-compatible properties
app: KoaXApplication
req: IncomingMessage
res: ServerResponse
request: KoaXRequest
response: KoaXResponse
state: Record<string, any>
// Delegated from request
url: string
method: string
path: string
query: Record<string, string>
headers: Record<string, string | string[] | undefined>
// Delegated from response
status: number
message: string
body: any
// Methods
throw(status: number, message?: string): never
assert(condition: any, status: number, message?: string): void
set(field: string, val: string | string[]): void
get(field: string): string | number | string[] | undefined
// NEW: KoaX additions
log: Logger // Structured logger with request context
requestId: string // Unique request ID
startTime: number // Request start timestamp
}Logger
// Log levels
ctx.log.trace(msg: string, data?: object): void
ctx.log.debug(msg: string, data?: object): void
ctx.log.info(msg: string, data?: object): void
ctx.log.warn(msg: string, data?: object): void
ctx.log.error(err: Error | string, data?: object): void
ctx.log.fatal(msg: string, data?: object): void
// Alternative signature
ctx.log.info({ userId: 123 }, 'User logged in');Transports
import { transports } from '@bamptee/koax';
// Console
transports.console({ prettyPrint: boolean })
// File
transports.file(path: string, options?: {
bufferSize?: number
flushIntervalMs?: number
})
// HTTP
transports.http(url: string, options?: {
headers?: Record<string, string>
bufferSize?: number
flushIntervalMs?: number
})
// Custom
transports.custom((entry: LogEntry) => void)
// Multi
transports.multi(...transports: Transport[])
// Filter
transports.filter(
transport: Transport,
filter: (entry: LogEntry) => boolean
)🎓 Examples
Basic REST API
import KoaX from '@bamptee/koax';
const app = new KoaX({ logger: { prettyPrint: true } });
app.use(async (ctx, next) => {
try {
await next();
} catch (err: any) {
ctx.log.error(err, 'Request failed');
ctx.status = err.status || 500;
ctx.body = { error: err.message, requestId: ctx.requestId };
}
});
app.use(async (ctx) => {
const { path, method } = ctx;
if (path === '/api/users' && method === 'GET') {
ctx.log.info('Fetching users');
const users = await db.users.findAll();
ctx.body = { users };
return;
}
if (path === '/api/health' && method === 'GET') {
ctx.body = { status: 'healthy', uptime: process.uptime() };
return;
}
ctx.status = 404;
ctx.body = { error: 'Not Found' };
});
app.listen(3000);With Existing Koa Middleware
import KoaX from '@bamptee/koax';
import cors from '@koa/cors';
import bodyParser from 'koa-bodyparser';
import Router from '@koa/router';
const app = new KoaX();
const router = new Router();
// Use Koa middleware - works unchanged!
app.use(cors());
app.use(bodyParser());
// Use Koa router - works unchanged!
router.get('/users', async (ctx) => {
ctx.body = { users: [] };
});
router.post('/users', async (ctx) => {
const user = ctx.request.body;
ctx.body = { user };
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);Production Setup
import KoaX, { transports } from '@bamptee/koax';
const app = new KoaX({
contextPoolSize: 2000,
logger: {
enabled: true,
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
prettyPrint: process.env.NODE_ENV !== 'production',
name: 'my-api',
transport: process.env.NODE_ENV === 'production'
? transports.multi(
// All logs to file
transports.file('/var/log/app.log'),
// Errors to monitoring
transports.filter(
transports.http(process.env.LOG_ENDPOINT!, {
headers: { 'Authorization': `Bearer ${process.env.LOG_TOKEN}` }
}),
(entry) => entry.level >= 50
)
)
: transports.console({ prettyPrint: true })
}
});
// Security headers
app.onRequest(async (ctx) => {
ctx.set('X-Frame-Options', 'DENY');
ctx.set('X-Content-Type-Options', 'nosniff');
ctx.set('X-XSS-Protection', '1; mode=block');
});
// Request logging
app.onRequest(async (ctx) => {
ctx.log.info('Request received', {
ip: ctx.req.socket.remoteAddress,
userAgent: ctx.headers['user-agent']
});
});
// Metrics
app.onResponse(async (ctx) => {
const duration = Date.now() - ctx.startTime;
metrics.histogram('http_request_duration', duration, {
method: ctx.method,
path: ctx.path,
status: ctx.status
});
});
// Error alerting
app.onError(async (error, ctx) => {
if (error.status >= 500) {
alerting.sendAlert({
severity: 'error',
message: error.message,
requestId: ctx.requestId,
path: ctx.path
});
}
});
// Your routes...
app.use(routes);
const PORT = parseInt(process.env.PORT || '3000', 10);
app.listen(PORT, () => {
app.logger.info(`Server running on port ${PORT}`);
});📚 Documentation
- QUICKSTART.md — 5-minute setup guide
- HOOKS_AND_LOGGING.md — Complete hooks & logging guide
- TRANSPORTS.md — Transport system documentation
- COMPARISON.md — Side-by-side Koa comparison
- OPTIMIZATIONS.fr.md — Technical optimizations (FR)
- IMPROVEMENTS_SUMMARY.md — Feature summary
🔄 Migration from Koa
Step 1: Install
npm uninstall koa
npm install koaxStep 2: Update Imports
// Before
import Koa from 'koa';
// After
import KoaX from '@bamptee/koax';Step 3: Update Instantiation (Optional)
// Before
const app = new Koa();
// After (minimal)
const app = new KoaX();
// After (with features)
const app = new KoaX({
logger: { enabled: true, prettyPrint: true }
});Step 4: Add Hooks (Optional)
// Add logging hooks
app.onRequest(async (ctx) => ctx.log.info('Request received'));
app.onResponse(async (ctx) => ctx.log.info('Response sent'));That's it! All your Koa middleware works unchanged.
🆚 KoaX vs Koa
| Feature | Koa | KoaX | |---------|-----|------| | API Compatibility | ✅ | ✅ 100% | | Middleware | ✅ | ✅ Same | | Performance | Good | +23% faster | | Context Pooling | ❌ | ✅ Built-in | | Hooks System | ❌ | ✅ Built-in | | Structured Logging | ❌ | ✅ Built-in | | Log Transports | ❌ | ✅ Built-in | | Request Timing | ❌ | ✅ Built-in | | Request ID | ❌ | ✅ Built-in | | TypeScript | Partial | ✅ Full | | Dependencies | Several | Zero extra |
🤝 Compatibility
Works With
- ✅ All Koa middleware (
@koa/cors,koa-bodyparser, etc.) - ✅
@koa/routerand other routers - ✅ Node.js 18+
- ✅ TypeScript 5.0+
- ✅ CommonJS and ESM
Tested With
@koa/cors✅koa-bodyparser✅@koa/router✅koa-session✅koa-static✅- Custom middleware ✅
🧪 Testing
# Run tests
npm test
# Run examples
npm run dev # Router example
npm run dev:hooks # Hooks example
npm run dev:transports # Transports example
# Run benchmarks
npm run benchmark # Koa vs KoaX
npm run benchmark:hooks # Overhead analysis🤔 FAQ
Why KoaX instead of Koa?
- Better performance — 23% faster out of the box
- Better DX — Hooks, logging, timing built-in
- Better observability — Request IDs, structured logs, transports
- Production-ready — Error handling, monitoring, metrics
- Same API — Drop-in replacement, no rewrite needed
Is it really 100% compatible?
Yes! All Koa middleware works unchanged. We maintain the same:
- Middleware signature:
(ctx, next) => Promise<void> - Context API (with additions)
- Onion model (downstream/upstream)
- Error handling
What's the performance cost of hooks/logging?
~11% overhead for both hooks and logging. Worth it for the observability gains.
You can disable features you don't need:
const app = new KoaX({
logger: { enabled: false }, // Disable logger
timing: false // Disable timing
});Can I use it with my existing Koa app?
Yes! Just replace import Koa from 'koa' with import KoaX from '@bamptee/koax'. Everything else works the same.
Does it work with TypeScript?
Yes! KoaX is written in TypeScript and has complete type definitions.
🗺️ Roadmap
- [x] Context pooling
- [x] Iterative middleware dispatch
- [x] Hooks system
- [x] Structured logging
- [x] Transport system
- [x] Request timing
- [x] Request ID tracking
- [ ] Schema validation (AJV)
- [ ] WebSocket support
- [ ] HTTP/2 & HTTP/3
- [ ] OpenTelemetry integration
- [ ] Metrics endpoint
- [ ] Rate limiting
- [ ] CORS middleware (built-in)
📄 License
MIT © 2025
🙏 Credits
KoaX is inspired by:
🌟 Show Your Support
If you like KoaX, please:
- ⭐️ Star this repo
- 🐛 Report issues
- 💡 Suggest features
- 🔀 Contribute PRs
Built with ❤️ for the Node.js community
