@nxtedition/http
v2.0.0
Published
HTTP/HTTP2 server middleware framework with priority-based scheduling, request lifecycle tracking, and Koa-style middleware composition.
Maintainers
Keywords
Readme
@nxtedition/http
HTTP/HTTP2 server middleware framework with priority-based scheduling, request lifecycle tracking, and Koa-style middleware composition.
Install
npm install @nxtedition/httpUsage
Basic Server
import { createServer } from '@nxtedition/http'
const server = createServer({ keepAliveTimeout: 120_000 }, { logger: pino() }, (ctx) => {
ctx.res.writeHead(200, { 'content-type': 'text/plain' })
ctx.res.end('Hello World')
})
server.listen(3000)Middleware Composition
import { createServer, compose } from '@nxtedition/http'
const auth = async (ctx, next) => {
if (!ctx.req.headers.authorization) {
const err = new Error('Unauthorized')
err.statusCode = 401
err.expose = true
throw err
}
await next()
}
const handler = async (ctx) => {
ctx.res.writeHead(200)
ctx.res.end(JSON.stringify({ user: 'authenticated' }))
}
const server = createServer({ logger: pino() }, [auth, handler])Priority-Based Scheduling
Requests are automatically prioritized based on the nxt-priority header or user-agent detection. Priority maps to DSCP/ToS values on the socket:
| Priority | Value | DSCP | Use Case | | -------- | ----- | ---- | ---------------- | | highest | 3 | EF | Realtime | | higher | 2 | AF41 | High priority | | high | 1 | AF31 | User experience | | normal | 0 | BE | Default traffic | | low | -1 | LE | Background tasks | | lower | -2 | LE | Replication | | lowest | -3 | LE | Batch jobs |
Error Handling
Thrown errors are automatically serialized to HTTP responses:
const handler = (ctx) => {
const err = new Error('Validation failed')
err.statusCode = 422
err.body = { errors: [{ field: 'email', message: 'required' }] }
err.headers = { 'x-request-id': ctx.id }
throw err
}Error properties:
statusCode- HTTP status code (default: 500)body- Response body (object, string, or Buffer)headers- Additional response headers (object or flat array)expose- Iftrue, includeserr.messageas JSON response body
Context Factory
import { createServer, Context } from '@nxtedition/http'
const server = createServer((req, res) => {
const ctx = new Context(req, res, logger)
ctx.db = database
return ctx
}, handler)AbortSignal Support
const handler = async (ctx) => {
// ctx.signal is aborted when the request errors or is aborted
const data = await fetch('http://upstream/api', { signal: ctx.signal })
ctx.res.writeHead(200)
ctx.res.end(await data.text())
}API
createServer(ctx, middleware?)
createServer(options, ctx, middleware?)
Creates an HTTP server with enhanced request/response classes.
- options - Server options (extends
http.ServerOptions)logger- Pino-compatible loggersignal-AbortSignalfor graceful shutdownsocketTimeout/timeout- Socket timeout (default: 1h)
- ctx - Context config object
{ logger, ...opaque }or factory function(req, res) => Context - middleware - Handler function or array of middleware functions
compose(...middleware)
Composes middleware functions into a single middleware. Supports arrays and variadic arguments. In development mode, validates that next() is not called multiple times and that middleware properly awaits downstream.
Context
Request context wrapping req and res with:
id- Unique request IDreq-IncomingMessageinstanceres-ServerResponseinstancetarget- Parsed URL target (protocol, hostname, port, pathname, search)query- Parsed query parametersuserAgent- User-Agent header valuepriority- Resolved request prioritysignal-AbortSignal(lazily created)logger- Child logger with request ID
IncomingMessage
Extended http.IncomingMessage with computed id, target, and query properties. Results are lazily cached and invalidated when URL or host changes.
ServerResponse
Extended http.ServerResponse with:
timing- Request timing metrics (created, connect, headers, data, end)bytesWritten- Total bytes written to response- Custom header tracking that bypasses Node.js per-header bookkeeping. This is a trusted-caller fast path, not a defensive copy of Node's header API.
The custom response implementations require these invariants:
- Header names passed to
setHeader,appendHeader,removeHeader,getHeader, andhasHeader, and names in awriteHeadheader object, are already lowercase and valid HTTP field names. - Header values are valid strings, numbers, or string arrays. Appending to a
numeric value may create a response-owned mixed array before serialization.
undefinedis supported only bysetHeader, where it removes the field. - Header mutation finishes before
writeHead. Raw flat header arrays are not supported; use the header object form instead. getHeadersreturns the live internal null-prototype map. Before any header storage is allocated,getHeaderNamesreturns a shared frozen empty array. Treat returned storage as borrowed and read-only.- Arrays passed to
setHeaderbecome response-owned. In particular,appendHeadermay extend the same array in place, so callers must not freeze or subsequently mutate it. - Do not mix the HTTP/2 response header API with direct
stream.respondcalls, and do not pass HTTP/2 pseudo-headers or connection-specific fields through the custom API. - Only explicitly staged fields are tracked. Removing a field such as
datedoes not disable headers that Node may add implicitly during serialization.
Outside production, these invariants are asserted and names and values are
validated to catch caller bugs early. With NODE_ENV=production, the hot path
skips assertions, normalization, validation, cloning, and defensive merging.
Violating the contract in production has undefined behavior and no rollback
guarantee. Headers are handed to Node only at writeHead time, where Node's
final serializer still enforces wire-level validity once.
requestMiddleware(ctx, next)
Core middleware that handles request lifecycle:
- Sets
request-idresponse header - Fast-dumps GET/HEAD request bodies
- Tracks pending requests
- Serializes errors to HTTP responses
- Logs request lifecycle events
upgradeMiddleware(ctx, next)
Middleware for WebSocket/upgrade request handling with lifecycle logging.
genReqId()
Generates unique request IDs using V8 SMI-optimized integers (req-{base36}).
License
MIT
