DEV Community

Cover image for NEXT.JS 16.1 MIGRATION: REFACTORING MIDDLEWARE.TS TO PROXY.TS (WITHOUT BREAKING AUTH)
BeyondIT
BeyondIT

Posted on

NEXT.JS 16.1 MIGRATION: REFACTORING MIDDLEWARE.TS TO PROXY.TS (WITHOUT BREAKING AUTH)

You ran npm install next@latest, typed the upgrade command, and watched your terminal explode. Or maybe you just saw the deprecation warning in Next.js 16.1 and felt that familiar pit in your stomach.

It’s not just you. Next.js 16.1 isn't just an upgrade; it’s a paradigm shift.

Vercel has officially renamed middleware.ts to proxy.ts. This isn't cosmetic—it's a hard pivot from the Edge Runtime back to Node.js, and it changes where your authentication logic belongs.

🚀 TL;DR / Full Guide: This article covers the basic syntax migration. For the advanced survival guidecovering Supabase Cookie SyncingAuth.js v5 wrappers, and Clerk migration patterns, read the full article on my blog:

👉 The Complete Next.js 16.1 Migration Guide (Beyond IT)

⚡ The Quick Fix (The Codemod)

If you just want to get your build passing, Vercel provides a codemod to handle the file renaming and export updates.

npx @next/codemod@canary middleware-to-proxy
Enter fullscreen mode Exit fullscreen mode

This will rename your file and change the export from middleware to proxy:

// proxy.ts (formerly middleware.ts)
import { NextRequest, NextResponse } from 'next/server';

// The export must be named 'proxy'
export function proxy(request: NextRequest) {
  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*'],
};
Enter fullscreen mode Exit fullscreen mode

🚨 The "Async Trap" (Why your build might still fail)

Next.js 16.1 aligns with React 19, which means you can no longer access cookies or headers synchronously.

If your old middleware looked like this, it will crash at runtime:

// ❌ BROKEN in 16.1
const cookieStore = cookies();
const token = cookieStore.get('token');
Enter fullscreen mode Exit fullscreen mode

You must update every instance to use await:

// ✅ FIXED
const cookieStore = await cookies();
const token = cookieStore.get('token');
Enter fullscreen mode Exit fullscreen mode




🛑 The "Where Does Auth Go?" Crisis

This is where things get tricky. proxy.ts now defaults to the Node.js runtime. It is designed to be a "Thin Proxy" for redirects and rewrites, NOT for heavy database calls or complex JWT validation.

If you are using SupabaseAuth.js, or Clerk, your authentication flow likely needs a refactor to avoid the "Logout Loop" bug (where users try to sign out but the cookie never clears because the proxy didn't pass the response header).

⚠️ Fixing the Logout Loop: The solution requires creating a mutable response object and manually syncing cookies between the request and response. I've documented the exact code patterns for Supabaseand Auth.js in the full guide.

👉 Get the Library-Specific Code Snippets Here

The New Architecture

To avoid bottlenecks, you need to split your logic:

  1. In proxy.ts: Perform "optimistic" checks. Does the session cookie exist? If no, redirect to login.

  2. In Server Components: Perform the actual validation (database lookups).

Summary

The shift to proxy.ts forces a cleaner architecture, but the migration path is full of undocumented traps regarding authentication libraries.

If you are stuck debugging a specific auth provider, check out the full deep dive on Beyond IT, where I cover the exact implementations for:

  • ✅ Supabase (Fixing the infinite loop)

  • ✅ Auth.js (v5) (The correct wrapper config)

  • ✅ Clerk (The new middleware naming convention)

👉 Read the Full Next.js 16.1 Guide on Beyond IT

Top comments (0)