PAS7 Studio

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.

14 May 2026· 16 min read· Technology
Best forBackend engineersFull-stack developersTech leads evaluating Bun for APIsTeams migrating from Express/Fastify to Bun
Dark technical illustration of a request pipeline for Bun.js middleware with auth, logging, validation, and response layers

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.

We will unpack native Bun.serve without Express illusions.
We will compare Hono middleware and Elysia lifecycle hooks.
We will show use cases, optimization, best practices, and bad practices.
We will plan separate deep dive articles for auth, rate limiting, observability, and body parsing.

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.

A clear definition of middleware in the Bun context: native Bun.serve, Hono, Elysia, and the Express-compatible world. [1][2][5][6]
A mental model: pre-handler, short-circuit, post-handler, error handling, cleanup, response mapping.
Examples of framework-free middleware composition when you need minimal overhead.
Where Hono or Elysia are more practical than a custom pipeline.
Optimization: middleware order, body parsing, caching, response headers, static responses, timeouts, streams. [2][3][4]
Best practices and bad practices for auth, CORS, rate limiting, logging, validation, and observability.

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 positions Bun.serve as a high-performance HTTP server, and the routes API supports static responses, dynamic routes, method handlers, and fallback fetch. [1][2]
Bun docs on Bun.serve
Hono explicitly describes middleware as code that runs before/after the handler, can await next(), or return a Response for early exit. [5]
Hono docs on middleware
Elysia splits the request-response cycle into lifecycle events, where hooks are applied to routes in registration order. [6]
Elysia docs on lifecycle

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:

TS
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 screenshot

When 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.

ApproachWhen to choose itStrengthRisk
Native Bun.serveSmall services, gateways, webhooks, health endpoints, custom routingMinimal overhead, full control over Web Request/ResponseYou write routing, context, errors, and middleware order yourself
HonoAPIs that need familiar middleware, Web Standards, and portabilitySimple onion model, .use(), built-in and third-party middleware [5]It is easy to bring Express habits and add too many global .use() layers
ElysiaBun-first APIs, typed context, validation, plugins, lifecycle hooksClear 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 stackMigrating an existing Node app where middleware ecosystem matters more than runtime purityLower migration costSome 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 screenshot

Summary

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.

01

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.

02

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.

03

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]

04

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]

05

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.

Does Bun.js have built-in Express-style middleware?

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]

What is better for Bun middleware: Hono or Elysia?

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.

Which middleware affects performance the most?

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.

Can I read `req.json()` in middleware?

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.

How do I make Bun middleware safe for production?

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.

Should Express middleware be moved to Bun one-to-one?

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.

Reviewed: 14 May 2026Applies to: Bun 1.2.3+ routes APIApplies to: Bun 1.3.x runtimeApplies to: Hono 4.xApplies to: Elysia 1.xApplies to: TypeScript backend APIsTested with: Bun.serve routesTested with: Fetch API Request/ResponseTested with: Hono middlewareTested with: Elysia lifecycle hooksTested with: WebSocket timeout and backpressure controls

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.

You are here01/05

Bun.js middleware in 2026: overview, best practices, and anti-patterns

Previous
Next

Related Articles

ai-assistants

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.

blogs

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.

growth

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.

blogs

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.