Designing HTTP Servers Around Deno Runtime

Each File an Endpoint
Zero dependencies and zero config, just a routes folder on disk that becomes a typed HTTP server the moment Deno picks it up.
Save a file in the routes folder and the server picks it up on the next request, no restart, no watcher, no config, just the file system as source of truth for endpoints with types from the handler so the compiler catches mismatches before they land.
Every file dropped in routes shows up as a URL. The sidebar becomes a map of the API, each file a handler waiting for traffic.
Note
The framework is open source on GitHub at NeaByteLab/Deserve, with full documentation at docs-deserve.neabyte.com. MIT licensed and installable from JSR, it ships zero dependencies and turns a routes folder into typed endpoints the moment it starts.
Dead Weight in the Tree
Most HTTP frameworks for Deno fall into two camps, either shipping a dependency tree that fights the runtime or demanding config and boilerplate before the first route loads, leaving a gap for one treating the Deno std lib as the only dependency.
Opening a fresh project meant watching install logs scroll before a route loaded. Config files locked in choices before any handler existed. The Deno std lib ships primitives for routing, streaming, and request handling, so a framework reimplementing them felt like dead weight. Zero dependencies meant every line in the editor maps to runtime behavior, not a buried package.
The std lib does the heavy lifting. The framework just shapes it into a loop that feels natural to write and read in the editor.

Boilerplate Before the First Route
- Dependency trees fight the runtime and break on version bumps.
- Config files and boilerplate delay the first route from loading.
- Middleware scoping usually means global or nothing, no path control.
- Rendering and streaming often pull in a third-party template library.
Routes Map Themselves
- Map a routes directory to URLs, params, and HTTP methods automatically.
- Flow a typed Context through every handler for request and response work.
- Scope middleware globally, per route, or under a path prefix.
- Stream HTML, server-sent events, and NDJSON from the same pipeline.
- Ship auth, CORS, CSRF, and observability as opt-in middleware.
Typed Context Flows Through
The strategy is convention over configuration. A routes directory becomes the API surface, file names map to URLs, and exported functions become HTTP methods. A typed Context object designed to carry request reading, response sending, and error handling flows through every handler, while a composable middleware pipeline keeps the lifecycle clean without a config file.
Convention over configuration keeps the editor quiet, no config file demands decisions before the first route loads, no boilerplate blocks opening a file to write a handler, file names map to URLs, exports to methods, the framework wires the rest.
A typed Context carries request reading, response sending, and error handling through every handler. Middleware composes around it, scoping auth, CORS, and logging to routes that need them. The pipeline stays readable, each layer a small function. Streaming HTML, server-sent events, and NDJSON flow through the same shape, so rendering and real-time data share one path.

Save and Watch It Reload
Every route file becomes an endpoint the moment it lands on disk. A file-based router designed to scan the routes directory turns names into URL patterns, dynamic params, and HTTP methods, while a hot reload layer applies changes without restart. A view engine designed for server-side rendering streams HTML, cutting time-to-first-byte with no third-party template library.
// Route file becomes an endpoint by export
export function GET(ctx: Context): Response {
return ctx.send.json({ ok: true })
}
Security and observability ship as opt-in middleware. Basic auth, CORS, CSRF, security headers, body limits, and HMAC-SHA256 sessions snap into the pipeline, while an observability bus designed to emit lifecycle events feeds logs, errors, and audit.

Surface Shipped, Core Small
| Feature | Delivered |
|---|---|
| Routing | File-based with dynamic params and method exports |
| Context | Typed request reading, response sending, errors |
| Middleware | Global, route-specific, and path-prefixed scoping |
| Rendering | Built-in view engine with streaming HTML |
| Observability | Lifecycle events feeding logs, errors, and audit |
Directory as the Only Config
- Treating the file system as the only config removed an entire class of setup.
- A typed Context kept request and response work in one ergonomic object.
- Opt-in middleware kept the core small and let teams opt into complexity.
- Streaming from the same pipeline kept rendering and real-time data aligned.
Each Handler a File Away
What began as a way to stop wiring boilerplate became a framework treating the Deno runtime as the only dependency and the file system as the only config, where routes, middleware, and observability share one Context keeping loops and surface small.
The editor stays quiet, nothing extra to maintain. Files map to endpoints, exports to methods, and the runtime handles the rest.
Every handler sits one file away from a URL and every middleware a small function with a clear job, while the Deno std lib does the heavy lifting, visible from first import to last response, with nothing hidden in config or buried in dependencies.