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

src/main.tsbackend
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

src/main.tsbackend
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 bootstrap

Hono

src/main.tsbackend
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

src/main.tsbackend
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: