Node.js — framework adapters
The same pool + worker pair drops into any Node HTTP framework. Same five routes in each example: POST /api/incidents/seed, GET /seed-progress, GET /stats, GET /query?severity=critical&status=open, GET /:id. Only the route syntax differs — the pool and worker files (Node.js) are identical across all four.
Express
import express from 'express';
import { incidents, parseQueryArgs, readIncident, seedProgress } from './incidents';
const app = express();
app.post('/api/incidents/seed', async (_req, res) => {
res.json({ seeded: true, ms: await incidents.seedIncidents() });
});
app.get('/api/incidents/seed-progress', (_req, res) => {
res.json({ progress: seedProgress() });
});
app.get('/api/incidents/stats', async (_req, res) => {
res.json(await incidents.computeMetrics());
});
app.get('/api/incidents/query', async (req, res) => {
res.json(await incidents.queryIncidents(parseQueryArgs(req.query)));
});
app.get('/api/incidents/:id', (req, res) => {
const rec = readIncident(Number(req.params.id));
if (!rec) { res.status(404).json({ error: 'out of range' }); return; }
res.json(rec);
});
const server = app.listen(port, () => {
void incidents.seedIncidents().catch(console.error); // seed on bootstrap
});
process.on('SIGINT', () => { pool.terminate(); server.close(() => process.exit(0)); });Fastify
import Fastify from 'fastify';
import { incidents, parseQueryArgs, readIncident, seedProgress } from './incidents';
const app = Fastify();
app.post('/api/incidents/seed', async () => ({
seeded: true, ms: await incidents.seedIncidents(),
}));
app.get('/api/incidents/seed-progress', async () => ({ progress: seedProgress() }));
app.get('/api/incidents/stats', async () => incidents.computeMetrics());
app.get('/api/incidents/query', async (req) =>
incidents.queryIncidents(parseQueryArgs(req.query as Record<string, unknown>)));
app.get('/api/incidents/:id', async (req, reply) => {
const rec = readIncident(Number((req.params as { id: string }).id));
if (!rec) return reply.code(404).send({ error: 'out of range' });
return rec;
});
await app.listen({ port });
void incidents.seedIncidents().catch(console.error); // seed on bootstrapHono
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { incidents, parseQueryArgs, readIncident, seedProgress } from './incidents';
const app = new Hono();
app.post('/api/incidents/seed', async (c) =>
c.json({ seeded: true, ms: await incidents.seedIncidents() }));
app.get('/api/incidents/seed-progress', (c) => c.json({ progress: seedProgress() }));
app.get('/api/incidents/stats', async (c) => c.json(await incidents.computeMetrics()));
app.get('/api/incidents/query', async (c) =>
c.json(await incidents.queryIncidents(parseQueryArgs(c.req.query()))));
app.get('/api/incidents/:id', (c) => {
const rec = readIncident(Number(c.req.param('id')));
if (!rec) return c.json({ error: 'out of range' }, 404);
return c.json(rec);
});
const server = serve({ fetch: app.fetch, port }, () => {
void incidents.seedIncidents().catch(console.error); // seed on bootstrap
});Koa
import Koa from 'koa';
import Router from '@koa/router';
import { incidents, parseQueryArgs, readIncident, seedProgress } from './incidents';
const app = new Koa();
const router = new Router();
router.post('/api/incidents/seed', async (ctx) => {
ctx.body = { seeded: true, ms: await incidents.seedIncidents() };
});
router.get('/api/incidents/seed-progress', (ctx) => {
ctx.body = { progress: seedProgress() };
});
router.get('/api/incidents/stats', async (ctx) => {
ctx.body = await incidents.computeMetrics();
});
router.get('/api/incidents/query', async (ctx) => {
ctx.body = await incidents.queryIncidents(parseQueryArgs(ctx.query));
});
router.get('/api/incidents/:id', (ctx) => {
const rec = readIncident(Number(ctx.params.id));
if (!rec) { ctx.status = 404; ctx.body = { error: 'out of range' }; return; }
ctx.body = rec;
});
app.use(router.routes()).use(router.allowedMethods());
const server = app.listen(port, () => {
void incidents.seedIncidents().catch(console.error); // seed on bootstrap
});Live examples
The repo ships all four under examples/. Start them via npm run dev in each dir or npm run serve:all from the repo root, then:
- Express (
http://localhost:3200/api/incidents/stats) —examples/express, dep:express - Fastify (
http://localhost:3201/api/incidents/stats) —examples/fastify, dep:fastify - Hono (
http://localhost:3202/api/incidents/stats) —examples/hono, dep:hono + @hono/node-server - Koa (
http://localhost:3203/api/incidents/stats) —examples/koa, dep:koa + @koa/router