Documentation menu

Node adapter (Express & Fastify)

Connect-style middleware for Node HTTP servers (@rebilder/gateway/node) — written against structural types, importing nothing from express, fastify, or node:http.

Zero framework imports, by structural typing

The adapter imports nothing from express, fastify, or even node:http. It is written against structural types (NodeRequestLike, NodeResponseLike) that describe only what the adapter reads and writes — any real Node request/response object satisfies them, so the gateway’s zero-external-deps invariant holds and no @types packages ride along.

Install & Express integration

terminal
npm install @rebilder/gateway        # pnpm add / yarn add
server.ts
import express from 'express'
import { createGatewayMiddleware } from '@rebilder/gateway/node'
import { gatewayConfig } from './gateway-config'   // same GatewayConfig as every adapter

const app = express()
app.use(createGatewayMiddleware(gatewayConfig))    // before your routes
// ... your existing routes serve HTML exactly as before

Semantics are handleRequest’s, mapped onto the middleware contract:

  • Markdown path (agent + matching source): status and headers copied from the core response (text/markdown; charset=utf-8, Vary: Accept, X-Rebilder-Path: markdown), body written with res.end(). next() is not called — the gateway answered. The body is buffered before writing (no streaming) — deliberately fine: rendered markdown is capped by maxBytes (default 5KB; trymumm runs 8KB), so there is nothing worth streaming.
  • Pass-through (null): next() — humans, crawlers, unmatched URLs, throwing sources; your pipeline runs untouched.
  • Any adapter error (e.g. an unparseable Host header): next(). The gateway never crashes a merchant’s server and never leaves a request hanging.

How the web-standard Request is built

The web-standard Request is built from the Node request: host from the Host header; protocol from x-forwarded-proto (first value — set by your proxy/LB) → Express’s req.protocolsocket.encrypted; and Express’s originalUrl preferred over url (mounted routers rewrite url; sources must see the real path). Repeated (array) headers are appended per value.

toWebRequest(nodeReq, options?) is exported for reuse if you want core handleRequest semantics against a raw Node request yourself — custom servers, other frameworks.

Fastify

Two options, no fastify-specific code in the package:

Option A — @fastify/middie
// Option A — Fastify's middleware compat layer (@fastify/middie):
import middie from '@fastify/middie'
await fastify.register(middie)
fastify.use(createGatewayMiddleware(gatewayConfig))
Option B — onRequest hook
// Option B — a 3-line onRequest hook over the raw req/res (no plugin needed):
const gateway = createGatewayMiddleware(gatewayConfig)
fastify.addHook('onRequest', (req, reply, done) => {
  gateway(req.raw, reply.raw, done) // markdown answered on res; otherwise done() continues
})

With Option B, Fastify’s routing never sees gateway-answered requests (the response is written on the raw socket), and every pass-through continues through done() into your normal routes.