0
0
NextJSframework~15 mins

Middleware.ts file convention in NextJS - Deep Dive

Choose your learning style9 modes available
Overview - Middleware.ts file convention
What is it?
Middleware.ts is a special file in Next.js projects that runs code before a request reaches your pages or API routes. It lets you control requests globally, like checking if a user is logged in or redirecting them. This file uses TypeScript for type safety and better code quality. Middleware runs on the edge, meaning it works very fast and close to the user.
Why it matters
Without middleware, you would have to repeat the same checks or logic in every page or API route, which is slow and error-prone. Middleware.ts centralizes this logic, making your app faster and easier to maintain. It also improves user experience by handling things like redirects or authentication early, before loading full pages.
Where it fits
Before learning middleware.ts, you should understand basic Next.js routing and TypeScript basics. After mastering middleware.ts, you can explore advanced edge functions, server components, and security patterns in Next.js.
Mental Model
Core Idea
Middleware.ts is a gatekeeper that runs code on every request before your app responds, letting you control or modify requests globally.
Think of it like...
Middleware.ts is like a security guard at the entrance of a building who checks everyone before they enter, deciding if they can go in, need to be redirected, or stopped.
┌───────────────┐
│ Incoming      │
│ Request       │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Middleware.ts │  <-- runs first, checks/modifies request
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Page or API   │  <-- runs after middleware if allowed
│ Route Handler │
└───────────────┘
Build-Up - 7 Steps
1
FoundationWhat is Middleware.ts in Next.js
🤔
Concept: Middleware.ts is a file that runs code on every request before your app responds.
In Next.js, placing a file named middleware.ts at the root or in the app directory lets you run code on every incoming request. This code can inspect or change the request or response, like redirecting users or adding headers.
Result
Your app can handle requests globally, without repeating code in every page or API route.
Understanding middleware.ts as a global request handler helps you see how to centralize common logic and improve app performance.
2
FoundationBasic Middleware.ts Structure and Export
🤔
Concept: Middleware.ts exports a function named middleware that receives a request and returns a response or next action.
A minimal middleware.ts looks like this: import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // Your logic here return NextResponse.next(); } This function runs on every request and must return a NextResponse to continue or redirect.
Result
Middleware runs and lets the request continue to the page or API route.
Knowing the required function signature and return type is key to writing valid middleware that Next.js accepts.
3
IntermediateUsing Middleware.ts for Redirects
🤔Before reading on: do you think middleware can stop a request and send a redirect? Commit to yes or no.
Concept: Middleware.ts can redirect requests to other URLs before reaching the page or API route.
You can check conditions in middleware and return a redirect response: export function middleware(request: NextRequest) { if (!request.cookies.get('user-token')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } This sends users without a token to the login page.
Result
Users without the token are redirected before loading protected pages.
Understanding middleware as a gatekeeper that can redirect users early improves security and user flow.
4
IntermediateConfiguring Middleware Matching Patterns
🤔Before reading on: do you think middleware runs on every URL by default or only on specified paths? Commit to your answer.
Concept: You can control which requests middleware runs on by specifying matcher patterns in middleware.ts.
Add a config export to limit middleware to certain paths: export const config = { matcher: ['/dashboard/:path*', '/profile'], }; This runs middleware only on URLs starting with /dashboard or exactly /profile, skipping others.
Result
Middleware runs only on specified routes, improving performance and focus.
Knowing how to limit middleware scope prevents unnecessary processing and keeps your app efficient.
5
IntermediateAccessing Request Data in Middleware.ts
🤔
Concept: Middleware.ts can read request details like headers, cookies, and URL to make decisions.
The NextRequest object provides methods: - request.nextUrl: URL object of the request - request.cookies: access cookies - request.headers: read headers Example: const userAgent = request.headers.get('user-agent'); const token = request.cookies.get('auth-token');
Result
Middleware can customize behavior based on request info, like device type or login status.
Understanding request data access lets you build smarter middleware that adapts to user context.
6
AdvancedMiddleware.ts Runs on the Edge Runtime
🤔Before reading on: do you think middleware runs on the same server as your Next.js app or on a special edge environment? Commit to your answer.
Concept: Middleware.ts runs on the edge runtime, a lightweight environment close to users for fast response.
Next.js middleware runs on the V8 JavaScript engine at the edge, not in Node.js. This means: - Limited APIs (no filesystem access) - Fast cold starts - Runs globally near users You must write middleware code compatible with this environment.
Result
Middleware executes quickly and globally, improving app speed and scalability.
Knowing middleware runs on the edge explains why some Node.js features are unavailable and why performance is better.
7
ExpertAdvanced Middleware.ts Patterns and Pitfalls
🤔Before reading on: do you think stacking multiple middleware files in nested folders runs them all or only the closest one? Commit to your answer.
Concept: Middleware.ts supports advanced patterns like chaining, conditional logic, and careful state handling, but has pitfalls like cold start delays and limited APIs.
You can place middleware.ts in nested folders to scope middleware, but only one middleware runs per request path. Middleware must be stateless and fast to avoid delays. Avoid heavy computations or blocking calls. Use NextResponse.rewrite to modify URLs without redirecting. Example advanced use: if (request.nextUrl.pathname.startsWith('/api')) { // special API logic } return NextResponse.next();
Result
Middleware can handle complex routing and logic but requires careful design to avoid performance issues.
Understanding middleware limitations and patterns helps build robust, scalable apps and avoid common production bugs.
Under the Hood
When a request arrives, Next.js intercepts it and runs the middleware.ts function on the edge runtime before any page or API code. Middleware receives a NextRequest object representing the request and returns a NextResponse to continue, redirect, or rewrite. This runs on V8 isolates close to the user, minimizing latency. Middleware cannot access Node.js APIs like filesystem or database directly. Instead, it uses web-standard APIs and Next.js helpers.
Why designed this way?
Middleware was designed to run on the edge to improve performance and scalability by handling requests globally and early. Running on V8 isolates avoids cold start delays of full Node.js servers and reduces server load. The limited API surface enforces fast, stateless code, preventing slowdowns. This design balances power and speed for modern web apps.
┌───────────────┐
│ User Request  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Edge Runtime  │  <-- runs middleware.ts function
│ (V8 Isolate)  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Next.js Server│  <-- runs page or API handler
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does middleware.ts run on every single request by default? Commit to yes or no.
Common Belief:Middleware.ts runs on every request to the app automatically.
Tap to reveal reality
Reality:Middleware.ts runs on every request only if no matcher config limits it. You can specify paths to run middleware selectively.
Why it matters:Assuming middleware runs everywhere can cause unexpected slowdowns or logic errors if you forget to limit its scope.
Quick: Can middleware.ts access Node.js APIs like filesystem? Commit to yes or no.
Common Belief:Middleware.ts can use all Node.js features since it runs in the Next.js server environment.
Tap to reveal reality
Reality:Middleware.ts runs on the edge runtime, which does not support Node.js APIs like filesystem or database connections.
Why it matters:Trying to use unsupported APIs causes runtime errors and breaks middleware functionality.
Quick: If you place multiple middleware.ts files in nested folders, do they all run for a request? Commit to yes or no.
Common Belief:All middleware.ts files in the folder path run in order for each request.
Tap to reveal reality
Reality:Only one middleware.ts runs per request path, the closest matching one. They do not stack or chain automatically.
Why it matters:Misunderstanding this leads to missing middleware logic or duplicated code.
Quick: Does returning NextResponse.next() in middleware always mean the request is unmodified? Commit to yes or no.
Common Belief:NextResponse.next() just passes the request unchanged to the next handler.
Tap to reveal reality
Reality:You can modify the request URL or headers before returning NextResponse.next(), so the request can be changed even when continuing.
Why it matters:Assuming NextResponse.next() means no changes can cause confusion about middleware effects.
Expert Zone
1
Middleware runs on the edge with strict limits, so you must write pure, stateless functions without side effects or heavy computations.
2
Middleware can rewrite URLs internally without redirecting users, enabling seamless routing changes invisible to the browser.
3
Middleware ordering depends on folder structure and matcher specificity, which can cause subtle bugs if misunderstood.
When NOT to use
Middleware is not suitable for heavy data processing, database access, or long-running tasks. Use API routes or server functions for those. Also avoid middleware for client-only logic or UI rendering.
Production Patterns
In production, middleware is used for authentication checks, A/B testing routing, geo-based redirects, and adding security headers. Teams often combine middleware with edge caching for performance.
Connections
HTTP Interceptors
Middleware.ts acts like an HTTP interceptor that inspects and modifies requests before they reach the main handler.
Understanding HTTP interceptors in other frameworks helps grasp middleware's role as a global request filter.
Operating System Kernel Hooks
Middleware.ts is similar to kernel hooks that run code on system calls before the OS processes them.
Knowing kernel hooks shows how middleware can control system behavior early and globally.
Airport Security Checkpoints
Middleware.ts functions like airport security checking passengers before boarding, deciding who can proceed or must be redirected.
This connection highlights middleware's role in security and flow control in web apps.
Common Pitfalls
#1Running middleware on every route without limiting scope.
Wrong approach:export const config = {}; // no matcher specified, middleware runs everywhere
Correct approach:export const config = { matcher: ['/protected/:path*'], };
Root cause:Not understanding the matcher config causes middleware to run unnecessarily, hurting performance.
#2Using Node.js APIs like fs in middleware.ts.
Wrong approach:import fs from 'fs'; export function middleware() { const data = fs.readFileSync('file.txt'); return NextResponse.next(); }
Correct approach:export function middleware() { // Use only web APIs and Next.js helpers return NextResponse.next(); }
Root cause:Confusing edge runtime environment with Node.js server environment.
#3Expecting multiple middleware.ts files in nested folders to run in sequence.
Wrong approach:Having middleware.ts in / and /dashboard expecting both to run on /dashboard requests.
Correct approach:Place middleware.ts only in the folder matching the routes you want to handle, knowing only one runs per request.
Root cause:Misunderstanding middleware scoping and execution order.
Key Takeaways
Middleware.ts in Next.js runs code on every matching request before your app responds, acting as a global gatekeeper.
It runs on the edge runtime, which is fast but limits available APIs to web standards only.
You can control which requests middleware runs on using matcher patterns to improve performance.
Middleware can redirect, rewrite, or modify requests early, improving security and user experience.
Understanding middleware's environment and limitations is key to writing effective, bug-free code.