Bun.js middleware in 2026: what it is, how it works, how to optimize it, and where not to break your API
A practical guide to middleware in Bun.js: native Bun.serve, Hono, Elysia, auth, CORS, rate limiting, logging, observability, performance, best practices, and bad practices. We explain when middleware helps, when it creates latency, and how to build production-ready APIs on Bun.

Bun.js Middleware Production Guide 2026
A series about production middleware in Bun.js: overview, security, performance, observability, rate limiting, body parsing, WebSocket/SSE, and request pipeline testing.
All articles in this guide
01
Bun.js middleware in 2026: overview, best practices, and anti-patterns
The base mental model for middleware in native Bun, Hono, and Elysia, with examples, optimization, and a roadmap for the next deep dive articles.
02
Auth middleware in Bun.js: JWT, sessions, API keys, and multi-tenant context
How to build auth middleware in Bun correctly: check order, cache, token rotation, tenant context, 401/403 errors, and testing.
03
Rate limiting in Bun.js: in-memory, Redis, sliding window, and edge cases
A detailed breakdown of rate limiting for Bun APIs: algorithms, Redis, distributed limits, abuse protection, and graceful degradation.
04
Observability middleware in Bun.js: logs, request id, tracing, and latency budgets
How to add request id, structured logs, timing headers, an OpenTelemetry-like flow, and avoid turning logging into a bottleneck.
05
Body parsing and validation in Bun.js: JSON, uploads, streams, and payload limits
How to safely read request bodies in Bun, where to place limits, and how not to break streams, uploads, idempotency, and schema validation.
Bun can accept HTTP requests quickly, but it cannot magically fix a pipeline where every middleware reads the body, creates unnecessary objects, performs synchronous crypto, writes verbose logs, and fetches the user from the database for every public endpoint.
That is why good Bun middleware is not “add one more .use()”. It is request pipeline design: what gets checked before routing, what runs after routing, what can short-circuit, what has access to the body, what gets cached, what gets measured, and what should not be middleware at all.
If you are planning a Bun API in 2026, this article will help you avoid repeating Node/Express habits where Bun gives you a different model and better primitives.
Bun.serve without Express illusions.quick map
This is not a “hello world” tutorial. It is a practical overview for teams that want to build Bun APIs with a proper request pipeline instead of moving Express middleware chaos into a different runtime.
In classic Express thinking, middleware is a function between request and response that can mutate req, mutate res, call next(), or finish the response. In native Bun, the model is different: the base server works with the Web APIs Request and Response, and Bun.serve accepts a fetch handler or routes. So “middleware” in native Bun is not a built-in Express-like mechanism, but an architectural pattern of composing functions around a handler. [1][2]
This is an important difference. When you write native Bun, you decide what the pipeline looks like: an array of functions, a wrapper around a handler, a small router, or a full framework. Bun gives you a fast HTTP server, routes, static responses, file responses, timeouts, request IP, and WebSocket controls. But it does not force one middleware model on you. [2][3]
Hono and Elysia fill this layer differently. Hono calls middleware an “onion structure”: middleware runs before the handler, can await next(), and then modify the response after the handler. Elysia describes lifecycle events: request, parse, transform, beforeHandle, afterHandle, mapResponse, error, afterResponse, and trace. [5][6]
In short
Bun middleware is not one API. It has three practical modes: native composition around Bun.serve, Hono .use() for Web Standards middleware, or Elysia lifecycle hooks for a Bun-first framework approach.
The important part is not to separate middleware from runtime context. Bun optimizes the HTTP server and routes, Hono optimizes a simple middleware model, and Elysia optimizes a typed lifecycle. These are different answers to the same problem.
Bun positionsBun.serveas a high-performance HTTP server, and the routes API supports static responses, dynamic routes, method handlers, and fallbackfetch. [1][2]
Bun.serveHono explicitly describes middleware as code that runs before/after the handler, canawait next(), or return aResponsefor early exit. [5]
Elysia splits the request-response cycle into lifecycle events, where hooks are applied to routes in registration order. [6]
Summary
Practical takeaway: do not look for “one correct Bun middleware API”. First choose the lifecycle model that matches your product.
If the API is small, latency matters, and the team wants full control, a native Bun pipeline may be enough. The idea is simple: a handler accepts Request, returns Response, and middleware wraps the handler or runs sequentially before it.
The smallest useful pattern looks like this:
type Handler = (req: Request) => Response | Promise<Response>;
type Middleware = (next: Handler) => Handler;
const withRequestId: Middleware = (next) => async (req) => {
const requestId = crypto.randomUUID();
const res = await next(req);
res.headers.set("x-request-id", requestId);
return res;
};
const withSecurityHeaders: Middleware = (next) => async (req) => {
const res = await next(req);
res.headers.set("x-content-type-options", "nosniff");
res.headers.set("referrer-policy", "strict-origin-when-cross-origin");
return res;
};
const compose = (handler: Handler, middleware: Middleware[]) =>
middleware.reduceRight((next, layer) => layer(next), handler);
const app = compose(
async (req) => {
const url = new URL(req.url);
if (url.pathname === "/health") return new Response("OK");
return Response.json({ ok: true });
},
[withRequestId, withSecurityHeaders],
);
Bun.serve({ fetch: app });This approach is lightweight, but it gives you responsibility: you define order, errors, context, repeated body reads, route matching, typing, and testing yourself.
The base middleware pipeline model: cheap checks should reject the request before expensive handlers or body parsing.
Section native-bun-pattern screenshotWhen it fits
Native composition fits small APIs, internal services, health/readiness endpoints, custom gateways, and performance-sensitive routes. For a large product API, quickly evaluate Hono or Elysia.
In 2026, the question is not “does Bun have middleware?”. The question is which abstraction level you need and how much lifecycle behavior you want to maintain yourself.
| Approach | When to choose it | Strength | Risk |
|---|---|---|---|
Native Bun.serve | Small services, gateways, webhooks, health endpoints, custom routing | Minimal overhead, full control over Web Request/Response | You write routing, context, errors, and middleware order yourself |
| Hono | APIs that need familiar middleware, Web Standards, and portability | Simple onion model, .use(), built-in and third-party middleware [5] | It is easy to bring Express habits and add too many global .use() layers |
| Elysia | Bun-first APIs, typed context, validation, plugins, lifecycle hooks | Clear request lifecycle and typing in a Bun-oriented framework [6][7] | Hook registration order is critical, and the lifecycle needs to be understood deeply |
| Express-compatible stack | Migrating an existing Node app where middleware ecosystem matters more than runtime purity | Lower migration cost | Some middleware may depend on Node/Express details that must be tested |
Three practical middleware models for Bun APIs: native control, Hono onion middleware, or Elysia lifecycle hooks.
Section framework-choice screenshotSummary
If you are starting a new Bun API, do not automatically drag in the Express model. Native Bun, Hono, and Elysia offer different trade-offs between control, DX, typing, and ecosystem-ready middleware.
Good middleware solves a cross-cutting concern. Bad middleware hides business logic inside a global pipeline where no one can later explain why a route behaves the way it does.
Authentication and authorization
JWT/session/API key verification, user/tenant context normalization, early 401 or 403. But business-level permissions should live closer to the use case, not hide all authorization in global middleware.
CORS and security headers
A good candidate for middleware because the rules apply to many routes. Be careful not to set permissive * everywhere when the API works with credentials or private data.
Rate limiting and abuse protection
Middleware can quickly reject extra requests before body parsing and database calls. For distributed APIs, a local memory limit is not enough; you need shared storage or edge-level policy.
Observability
Request id, structured logs, duration, trace context, error mapping. This is one of the most useful middleware layers as long as it does not write excessive payloads or block the response path.
Body parsing and validation gate
Payload size limits, content-type checks, JSON parsing, schema validation. Remember: the body stream should not be blindly read multiple times in different layers.
Response normalization
A unified error format, cache headers, timing headers, compression decisions. But response middleware should not become a hidden serializer for all business logic.
Bun’s speed does not mean every middleware layer is automatically cheap. Pipeline optimization starts with execution rules, not with microbenchmarks.
Put cheap rejects before expensive operations
Method, path, content-type, origin, basic headers, and IP allowlist checks should run before body parsing, database calls, crypto, and remote config. This reduces CPU, memory pressure, and tail latency.
Do not read the body in global middleware without a reason
In the Web API model, the body is a stream. If middleware reads await req.json(), the handler can no longer read it as easily. Do body parsing in a narrow layer or pass the parsed body into context explicitly.
Use static responses for stable routes
Bun routes can accept ready Response objects. The official docs state that static route responses are cached for the lifetime of the server object and can provide a performance benefit for health checks, redirects, and fixed API responses. [2]
Separate middleware for public and private routes
Do not force /health, /ready, /assets/*, or public GET routes through auth, session lookup, and tenant hydration. In Bun routes, wildcard and specific routes let you explicitly separate these surfaces. [2]
Have a latency budget for every layer
Logging, auth, rate limit, validation, DB context, response mapping — each layer needs a budget. If middleware adds 5–10ms to every request, that is an architectural decision, not a detail.
Practical rule
An optimal Bun middleware pipeline is short, predictable, and measured. Anything that does not need to be global should not be global.
Not all optimizations are equally useful. Some reduce latency; others simply move complexity into a place that is harder to debug.
Good: static response for health/readiness
For /health, /ready, simple redirects, and fixed config responses, use route-level static responses. This reduces runtime work and removes unnecessary allocations. [2]
Careful: global auth middleware
Auth as a global layer is convenient, but it easily runs on routes that do not need it. It is better to explicitly group private APIs and keep public/system routes outside this pipeline.
Good: request id and timing
This is a cheap and useful baseline for production debugging. Just do not log full bodies or secrets.
Careful: body parsing before routing
If middleware reads the body for every request, you pay the cost even where the body is not needed. This hurts especially on uploads, webhooks, and streaming routes.
Summary
The best middleware optimization is not running middleware where it is not needed.
Bun has an important default behavior: HTTP idleTimeout is 10 seconds unless configured otherwise. For streaming responses or Server-Sent Events, the docs recommend setting a per-request timeout through server.timeout(req, 0) instead of increasing the global timeout for all requests. [3]
This directly affects middleware. If logging or auth middleware wraps a streaming response as if it were a normal JSON endpoint, you may accidentally buffer the response, close the stream, or make timeout policy identical for all routes.
For WebSockets, Bun has separate controls: idleTimeout, maxPayloadLength, backpressure through the result of .send(), and perMessageDeflate. Middleware thinking is different here too: connection upgrade and message lifecycle should not be mixed with HTTP JSON middleware. [4]
Rule for long connections
SSE, streams, and WebSockets should have a separate pipeline. Do not pass them through middleware that assumes a short request-response JSON flow.
These mistakes are familiar from the Node/Express world. In Bun, they do not become less harmful; they are just masked by a faster runtime.
Global middleware for everything: auth, body parsing, logging, rate limit, tenant lookup, and validation run even for /health.
Reading the body multiple times across layers without a clear contract for who owns the parsed payload.
Storing request-specific data in module-level variables. In a concurrent server, this is a direct path to leakage between requests.
Expensive synchronous work in middleware: crypto, filesystem, compression, large JSON transforms.
Logging full request bodies, cookies, authorization headers, or PII.
CORS * for private APIs with credentials.
Only in-memory rate limiting in multi-instance production.
No unified error format: one middleware returns text, another returns JSON, a third throws an exception.
Middleware changes the response in a way that prevents the handler from guaranteeing the contract.
The point
Middleware should make cross-cutting behavior explicit. If it hides business rules or state, the pipeline becomes more complex than the application code.
Use this list as a review checklist before launching a Bun API in staging or production.
Separate pipelines for public, private, system, and streaming routes
Do not force different traffic types through the same middleware stack.
Short-circuit as early as possible
Method/path/content-type/origin/rate limit should reject bad requests before expensive operations.
One body parsing owner
Define who reads the body, where the parsed value is stored, and how the handler receives it.
One error format
Auth, validation, rate limit, and internal errors should return a predictable JSON contract.
Request id everywhere
Add request id to response headers and structured logs so cross-service flows can be debugged.
Latency budget for every middleware
Measure middleware layer duration, not only total response time.
No secrets in logs
Authorization, cookies, API keys, tokens, and PII should be masked or not logged at all.
Tests for execution order
Test not only the happy path, but also early exit, error path, missing headers, invalid body, timeout, and streaming routes.
This post is the overview. Next, it makes sense to break down each middleware class separately, because auth, rate limiting, observability, and body parsing have different failure modes.
Chapter 2
Auth middleware
JWT, sessions, API keys, tenant context, cache, token refresh, 401 versus 403, and how not to hide permissions in a global layer.
Chapter 3
Rate limiting
Fixed window, sliding window, token bucket, Redis, distributed deployment, abusive clients, webhooks, and graceful degradation.
Chapter 4
Observability
Request id, structured logs, timing headers, tracing, error correlation, and how not to turn logging into a latency bottleneck.
Chapter 5
Body parsing and validation
JSON, multipart, uploads, streams, payload limits, schema validation, idempotency keys, and safe reuse of parsed body.
Summary
The series is useful as a production checklist for Bun APIs where middleware should be a controlled part of the architecture, not a decorative layer.
Bun gives you a strong HTTP runtime, fast routes, static responses, Web APIs, and low overhead. But middleware is where teams most often bring back all the complexity of the old Node stack: global side effects, body parsing in the wrong place, logging without a budget, auth without boundaries, and route behavior that cannot be explained from one file.
The right approach is simple: choose a lifecycle model, keep the pipeline short, split routes by traffic type, measure latency for every layer, do not read the body without ownership, and do not hide business rules in global middleware.
If you need minimal overhead, write native composition around Bun.serve. If you need a familiar middleware flow, use Hono. If you want a Bun-first typed lifecycle, look at Elysia. In all three cases, success depends not on the framework name, but on pipeline discipline.
Native `Bun.serve` does not impose an Express-style `.use()` model. It works through a `fetch` handler and `routes`, and middleware in native Bun is usually written as function composition around `Request`/`Response`. If you need a ready `.use()` model, use Hono; if you need lifecycle hooks, look at Elysia. [1][5][6]
Hono is better if you need a simple Web Standards middleware model, portability, and a familiar `.use()` flow. Elysia is better if you want a Bun-first framework, typed context, validation, and a clear lifecycle. Native Bun is better for small or highly controlled services.
The usual problems are body parsing, auth/session lookup, rate limiting with remote storage, verbose logging, synchronous crypto/compression, and middleware that runs globally on routes where it is not needed.
Yes, but carefully. In the Web API model, the body is a stream, so the middleware should be the explicit owner of parsing or pass the parsed value forward in context. Do not read the body across multiple layers without a contract.
Separate public/private/system/streaming routes, add early cheap checks, centralize the error format, do not log secrets, set payload limits, use request id, test early exits, and measure latency for every layer.
Not automatically. Some middleware will work through compatible frameworks or adapters, but Express middleware often depends on specific `req`/`res` mutations. For a new Bun API, first choose a native Bun, Hono, or Elysia model, then migrate only the layers you truly need.
The sources below confirm Bun API behavior, routes/static responses, server lifecycle controls, the Hono middleware model, and Elysia lifecycle hooks.
• 1. Bun docs - Server: `Bun.serve`, routes, fallback fetch, configuration, lifecycle methods
• 2. Bun docs - Routing: route precedence, type-safe params, static responses, file responses
• 3. Bun docs - Server idleTimeout and per-request `server.timeout(req, seconds)`
• 4. Bun docs - WebSockets: compression, backpressure, idle timeout, max payload length
• 5. Hono docs - Middleware guide: before/after handler, `await next()`, early response
• 8. Bun docs - Runtime options including `--max-http-header-size`
PAS7 Studio can help design a Bun backend, audit the request pipeline, split middleware by routes, and add observability, security baseline, and performance checks.
This is especially useful if you are migrating from Express/Fastify, launching a new Bun API, or trying to understand where middleware creates latency and risk.
Bun.js middleware in 2026: overview, best practices, and anti-patterns
You are here: 01/05
Bun.js middleware in 2026: overview, best practices, and anti-patterns
Related Articles
AI Assistant Development Cost in 2026: RAG Chatbots, CRM Integrations, Guardrails, and Support
A practical buyer guide to AI assistant development cost in 2026: prototypes, RAG chatbots, knowledge-base assistants, CRM and website integrations, guardrails, evaluations, monitoring, and support.
AI for landing page development: where it speeds up launches and where it hurts conversion
A practical research piece on using AI for landing page development: v0, Webflow AI, Builder.io, Framer-like builders, UX generation, copy, SEO, personalization, A/B testing, template risk, accessibility, security and technical debt.
AI SEO / GEO in 2026: Your Next Customers Aren’t Humans — They’re Agents
Search is shifting from clicks to answers. Bots and AI agents crawl, cite, recommend, and increasingly buy. Learn what AI SEO / GEO means, why classic SEO is no longer enough, and how PAS7 Studio helps brands win visibility in the agentic web.
The most powerful Apple chip yet? M5 Pro and M5 Max are breaking records
A data-backed March 2026 analysis of Apple M5 Pro and M5 Max. We break down why these chips can credibly be called Apple's most powerful pro laptop silicon, how they compare with M4 Pro, M4 Max, M1 Pro, M1 Max, and how they stack up against Intel and AMD laptop rivals.
Professional development for your business
We create modern web solutions and bots for businesses. Learn how we can help you achieve your goals.