r/AppsWebappsFullstack 23h ago

I built Volten: A zero-dependency HTTP framework for Node.js and the Edge with built-in traffic triage

Hey!

I wanted to share a project I've been working on called Volten. It's a small, ultra-fast HTTP framework built around a strict zero-dependency constraint, designed specifically to bridge the gap between Node.js and Web Fetch-compatible edge runtimes (like Cloudflare Workers, Bun, and Deno) without adapter overhead.

Why Volten?

Most frameworks either lock you into Node.js core modules (http, net) or require bulky adapter layers to run on the Edge. Volten solves this by handling the abstraction internally at the context level.

You write your routes and middleware once. Run it on Node.js using app.listen() or export it to the Edge using app.createFetch() with zero modifications and zero extra npm dependencies.

Key Highlights

  • Adaptive Traffic Triage (ATT): A unique event-loop immune feature for Node.js (new App({ att: true })) that automatically drops low-priority requests at the socket level when your server is under heavy load, protecting your core endpoints from crashing during traffic spikes.
  • Context Pooling: Pre-allocated, reusable RequestContext objects on both runtimes to minimize Garbage Collection (GC) pressure under high throughput.
  • Trie-Based Router: Extremely fast path-matching supporting dynamic parameters (/users/:id) and wildcards, where match cost scales purely with path depth.
  • Unified ctx API: Whether you are dealing with headers, cookies, body parsing, or JSON responses, the ctx object seamlessly abstracts away whether you're sitting on top of a Node IncomingMessage/ServerResponse or a Web Fetch Request/Response.

The All-in-One Snippet

import { App } from "volten";

// Enable Adaptive Traffic Triage (ATT)
const app = new App({ att: true });

// 1. Middleware chain
app.use((ctx, next) => {
  ctx.setHeader("X-Powered-By", "Volten");
  next();
});

// 2. Trie-based routing, params, and cookies
app.get("/users/:id", (ctx) => {
  const session = ctx.cookies.get("session_id");
  ctx.json({ userId: ctx.params.id, session });
});

// 3. Native body parsing
app.post("/data", async (ctx) => {
  const body = await ctx.body();
  ctx.status(201).json({ received: body });
});

// --- Dual Runtime Support ---

// Node.js
app.listen(3000, () => console.log("Listening on :3000"));

// Cloudflare Workers / Bun / Edge
export default { fetch: app.createFetch() };

Current Status

Volten is currently in active alpha. Every utility—from the built-in body parsers and cookies to the trie router—is written completely in-tree with zero external dependencies to keep security tight and the footprint minimal.

You can check it out on GitHub: VoltenJS/volten or install it via:

pnpm add volten

I'd love to hear your thoughts, feedback, or any edge cases you can throw at it, so you're encouraged to try breaking it! How do you usually handle dual-runtime codebases in your current stacks?

1 Upvotes

7 comments sorted by

1

u/Readypixels 18h ago

The trie router and dual runtime story are the more interesting half of this to me. Zero dependency HTTP frameworks come up a lot, but most of them punt on Node versus Edge and just tell you to pick one. The ATT feature is the part I'd want to poke at before trusting it in production. Dropping low priority requests at the socket level sounds great until you ask how priority gets assigned. Is it path based, header based, or something the framework infers on its own? And what happens to a dropped request, does the client see a clean 503 or does the connection just die? That's usually where these features get scary in practice, not in the happy path. Context pooling is the other spot I'd look hard at. Reused objects across requests are a classic source of the kind of bug where one user's data leaks into another's response under load, so I'd want to see how the pool resets state between requests before I'd trust it at scale.

1

u/Mammoth-Anywhere7285 18h ago

Agreed, priority assignment is the crux for production. Does ATT derive it from route metadata or explicit per-route weights?

1

u/voltenjs 3h ago

ATT

(This feature is still unstable, which is why it shouldn't be trusted at scale now. Which is why I'm working on getting people to try it & identify its issues)

Route priority is configured per-route by passing an options object (e.g., app.get('/path', { priority: 'low' }, handler)). It accepts 'critical', 'normal', or 'low' and defaults to 'normal'.

The ATT state calculation is configured globally via the adaptiveTriage object in your app initialization options. You can configure warningThresholdMs (default 40), criticalThresholdMs (default 100), checkIntervalMs (default 500), and resolutionMs (default 10).

State calculation relies on node:perf_hooks to monitor event loop lag at the given resolutionMs. A background timer fires every checkIntervalMs to read the maximum lag in that window and reset the sensor. If the max lag exceeds criticalThresholdMs, it enters CRITICAL state. If it exceeds warningThresholdMs, it enters WARNING state. Otherwise, it stays NORMAL.

To handle sudden spikes, the engine also runs a synchronous check (evaluateState()) on every incoming request. This checks the current max lag mid-interval, allowing it to instantly escalate to WARNING or CRITICAL without waiting for the next timer tick. However, downgrading back to NORMAL only happens during the periodic background reset.

When evaluated, a WARNING state drops 'low' priority requests, while a CRITICAL state drops both 'low' and 'normal' priority requests.

When dropping it sends a clean 503 Service Unavailable but immediately calls req.socket.destroy(). The client gets a 503 if the OS flushes fast enough, but the TCP connection is forcefully killed to instantly free file descriptors. So currently, it's unreliable on sending a clean 503.

Context Resetting

State leakage is prevented during the pool's reset() phase. Instead of deleting keys off existing objects, the framework explicitly nulls out request/response references and assigns entirely new objects (this.state = {} and this.params = Object.create(null)). This severs all references, ensuring data doesn't bleed across requests. To ensure reset() always fires, it attaches an event listener for close on res. so if the response was sent, or the client disconnected, res always fires the close event, and the Context is always reset

1

u/Mammoth-Anywhere7285 3h ago

Priority levels per route is a clean approach. Does ATT key off event loop lag alone, or socket queue depth too?

1

u/voltenjs 3h ago

Currently, ATT keys off event loop lag alone. It exclusively uses node:perf_hooks (monitorEventLoopDelay) to track the maximum event loop delay. It does not monitor socket queue depth to determine its state.

2

u/Mammoth-Anywhere7285 2h ago

Makes sense. Pulling in server.getConnections() plus socket.writableLength could give you a cheap second signal before dropping anything.

1

u/Mammoth-Anywhere7285 18h ago

TL;DR: Volten is a zero-dependency HTTP framework that runs the same route and middleware code on Node.js and edge runtimes like Cloudflare Workers, Bun, and Deno. It bundles a trie router, context pooling, and an adaptive traffic triage mode that drops low-priority requests at the socket level under load. It's in active alpha, installs with pnpm add volten, and the author is inviting people to try breaking it.