@ahadpvt.v3/reqex
v2.0.2
Published
Zero-dependency, CPU-optimised, environment-aware HTTP request parser for Node.js, Edge Runtime, Cloudflare Workers, AWS Lambda, TCP and UDP
Downloads
459
Maintainers
Readme
@ahadpvt.v3/reqex
A zero-dependency, CPU-optimised, environment-aware HTTP request parser — and now also a TCP and UDP context builder — built as the request layer beneath a universal route dispatcher.
Parses body, query, cookies, and headers on demand. Two explicit body reading paths — buffered for small payloads, streaming for large ones. TCP and UDP connections get the same ergonomic ctx shape as HTTP so shared middleware works across all protocols without branching.
Installation
npm install @ahadpvt.v3/reqexImport / Require
const { normalize, context, presets, adapters, adaptTcp, adaptUdp } = require("@ahadpvt.v3/reqex")Quick Start
const { parse } = require("@ahadpvt.v3/reqex")
const ctx = await parse(req)
console.log(ctx.method) // "POST"
console.log(ctx.path) // "/users/42"
console.log(ctx.query) // { page: "1" }
console.log(await ctx.body()) // { name: "Abdul" }How It Works
raw request (any env)
↓
normalize() — detects env, produces NormalizedRequest
↓
preset / parsers — parse only what you need
↓
context — rich object passed to your route handlerFor a dispatcher, use normalize() + preset directly:
const nr = normalize(req)
const ctx = await presets.api(nr)
console.log(ctx.query)
console.log(await ctx.body())Body Reading — Two Paths
Two explicit, mutually exclusive paths. Once one is used the other throws clearly.
Buffered — small payloads
const body = await ctx.body() // auto detect
const body = await ctx.body({ type: "json" }) // override
const body = await ctx.body({ limit: 512 * 1024 }) // 512kb limit
const buf = await ctx.rawBody() // raw BufferStreaming — large payloads
const stream = ctx.bodyStream() // raw Readable — zero buffering
stream.pipe(destination)
for await (const part of ctx.parts()) { // streaming multipart
if (part.file) part.stream.pipe(fs.createWriteStream(`/uploads/${part.filename}`))
else console.log(part.name, part.value)
}Consumption guard
await ctx.body()
ctx.bodyStream() // throws BODY_ALREADY_CONSUMED
ctx.bodyConsumed // boolean
ctx.bodyMode // "buffered" | "streaming" | "streaming-multipart" | nullPresets
| Preset | Body | Pre-warms | Best for |
|---|---|---|---|
| minimal | not touched | query, cookies, headers | routing, redirects |
| api | buffered json | query, headers | REST, GraphQL |
| auth | buffered json | query, cookies, headers, auth() | login, OAuth |
| form | buffered form/multipart | query, cookies, headers | HTML forms |
| upload | not pre-consumed | headers | file uploads — use ctx.parts() |
| proxy | not pre-consumed | headers | proxies — use ctx.bodyStream() |
| webhook | buffered raw + signature | headers | Stripe, GitHub, Twilio |
| full | buffered auto | everything | catch-all, debugging |
Context Object
ctx.method // "POST"
ctx.url // "https://example.com/api/users?page=1"
ctx.path // "/api/users"
ctx.host // "example.com"
ctx.subdomain // "api" or null
ctx.ip // real IP — CF > X-Real-IP > X-Forwarded-For > socket
ctx.env // "node" | "edge" | "lambda" | "express"
ctx.raw // original request, untouched
ctx.query // { page: "1" } — lazy, cached
ctx.cookies // { session: "abc" } — lazy, cached
ctx.headers // enriched headers — lazy, cached
ctx.bodyConsumed // boolean
ctx.bodyMode // string | null
ctx.get("x-request-id") // header, case-insensitive
ctx.is("json") // content-type check
ctx.hasBody() // true for POST/PUT/PATCH or content-length > 0
ctx.isPreflight() // CORS preflight
ctx.isUpgrade() // WebSocket upgrade
ctx.auth() // { type: "Bearer", token: "..." } | null
await ctx.body(opts) // buffered + parsed
await ctx.rawBody(opts) // buffered raw Buffer
ctx.bodyStream() // raw Readable
ctx.parts() // async iterator — streaming multipartTCP Context
For raw TCP, TLS, and Unix socket connections. Produced by adaptTcp(socket, data).
Has the same shape as HTTP ctx where safe — all HTTP-style accessors return safe empty values so shared middleware works across HTTP and TCP without branching.
const { adaptTcp } = require("@ahadpvt.v3/reqex")
net.createServer(socket => {
socket.on("data", chunk => {
const ctx = adaptTcp(socket, chunk)
console.log(ctx.ip) // "192.168.1.1"
console.log(ctx.port) // 54321
console.log(ctx.id) // "192.168.1.1:54321:1712345678000"
console.log(ctx.env) // "tcp"
console.log(ctx.data) // Buffer — raw chunk or decoded frame
console.log(ctx.socket) // net.Socket
})
})Used internally by dispex.tcp(), dispex.tls(), and dispex.unix().
UDP Context
For UDP messages. Produced by adaptUdp(msg, rinfo, socket).
const { adaptUdp } = require("@ahadpvt.v3/reqex")
const server = dgram.createSocket("udp4")
server.on("message", (msg, rinfo) => {
const ctx = adaptUdp(msg, rinfo, server)
console.log(ctx.ip) // sender IP
console.log(ctx.port) // sender port
console.log(ctx.data) // raw Buffer
console.log(ctx.env) // "udp"
})Used internally by dispex.udp().
Environments
adapters.detect(req) // auto
adapters.node(req) // http.IncomingMessage
adapters.express(req) // Express / Fastify
adapters.edge(req) // Edge Runtime / CF Workers
adapters.lambda(event) // AWS LambdaError Handling
try {
const body = await ctx.body()
} catch (err) {
if (err.isReqex) {
switch (err.code) {
case "BODY_TOO_LARGE":
case "BODY_ALREADY_CONSUMED":
case "INVALID_JSON":
case "NO_BOUNDARY":
case "STREAM_ERROR":
case "REQUEST_ABORTED":
case "INVALID_REQUEST":
case "UNKNOWN_PRESET":
}
}
}Constants
const { CONTENT_TYPE, DEFAULT_BODY_LIMIT, DEFAULT_UPLOAD_LIMIT } = require("@ahadpvt.v3/reqex")
CONTENT_TYPE.JSON // "application/json"
CONTENT_TYPE.GRPC // "application/grpc"
DEFAULT_BODY_LIMIT // 1048576 (1mb)
DEFAULT_UPLOAD_LIMIT // 52428800 (50mb)Runtime Adapter (Advanced)
You can inject runtime capabilities explicitly when running in constrained environments.
const { setRuntimeAdapter } = require("@ahadpvt.v3/reqex")
setRuntimeAdapter({
Buffer,
Readable,
nextTick: (fn) => queueMicrotask(fn),
reportError: (err, meta) => console.error("reqex runtime error", err, meta),
})If a required capability is missing, reqex throws UNSUPPORTED_RUNTIME with an explicit message.
Testing
npm test54 passed, 0 failedLicense
MIT © Abdul Ahad
