Nextjs/Tanstack:middleware
2026年6月3日middleware:
Nextjsに固有のものではなく、HTTPリクエストとレスポンスの間で処理を挟むレイヤー
リクエストがサーバーに到達する前に処理を挟むことが出来るのが特徴
Nextjs
proxy.ts
import { NextRequest } from "next/server";
export default function proxy(req: NextRequest) {
console.log("middleware", req);
return NextResponse.next();
}
export const config = {
matcher: [
"/edit/:path*",
"/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
],
};proxy()の{}内リクエストの前に実行する内容
config.matcher:proxyの内容を実行・除外するルート
:path*が付くと「後ろの階層にも適用」という意味長い行の部分はネガティブマッチャー。ここを書かないと
/api/*や_next/static/*にもproxyが走る
NextResponse.next():middlewareの処理をそのまま続けて通常通りにリクエストを通す(次に進める)ためのレスポンス
NextResponse.next()の部分が無くてもproxyは機能する
ライブラリ
様々なライブラリのDocsにはproxy.tsに書くコードが掲載されているが、大抵ミドルウェア関数がそのままexportされているだけなので、他のproxy処理を書く場所が無い。
next-intlの例:
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';
export default createMiddleware(routing);対策:ラッパー
const intlMiddleware = createMiddleware(routing);
export async function proxy(req: NextRequest) {
// 処理
return intlMiddleware(req);
}上記は自分の処理を行うために、ライブラリのミドルウェア(上記はintlMiddleware)をmiddlewareでラップしている
処理の流れは自分の処理の後にライブラリのミドルウェアが実行される
例
ルート保護
例:ユーザーがログインしていない場合/sign-inにリダイレクト。対象は /edit 以下
import { NextRequest, NextResponse } from "next/server";
import { auth } from "./lib/auth";
export default async function proxy(req: NextRequest) {
const session = await auth();
if (!session) {
return NextResponse.redirect(new URL("/sign-in", req.url));
}
return NextResponse.next();
}
export const config = {
matcher: [
"/edit/:path*",
"/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
],
};絶対URLとしてリダイレクト先を指定する
new URL():req.urlでドメイン部分(xxx.com)を取り出し、そこに左側の相対パスをくっつけてリダイレクト用の絶対URLを生成
言語の切り替え
*next-intlを使っている場合は内部的に自動で行われる
例:リクエストの前にブラウザのaccept-languageに基づいてリダイレクト
import { NextRequest, NextResponse } from "next/server";
export default function proxy(req: NextRequest) {
const pathname = req.nextUrl.pathname;
const lang = req.headers.get("accept-language") || "";
const locale = lang.includes("ja") ? "ja" : "en";
return NextResponse.redirect(new URL(`/${locale}${pathname}`, req.url));
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};*proxyで行うとユーザーが手動で言語を切り替える余地を与えない
ルートによる処理の分岐
以下のようにmatcherで複数パスの指定はできるが、ルート毎にproxy処理を分けたい場合はどうするか
export const config = {
matcher: ["/api/:path*", "/admin/:path*"],
};proxy内で分岐:
export function proxy(req: NextRequest) {
if (req.nextUrl.pathname.startsWith("/admin")) {
// /admin/* の処理
} else if (req.nextUrl.pathname.startsWith("/dashboard")) {
// /dashboard/* の処理
}
return NextResponse.next();
}req.nextUrl.pathnameでパスを確認して分岐処理を行う。共通処理は分岐の前に書く
Tanstack
createStart
start.ts:
import { createStart } from '@tanstack/react-start'
const csrfMiddleware = createCsrfMiddleware({
filter: (ctx) => ctx.handlerType === "serverFn",
});
export const startInstance = createStart(() => {
return {
requestMiddleware: [authMiddleware , csrfMiddleware],
}
})Nextjsのproxy.tsと同じ様なもの
全てのリクエストで実行される。
requestMiddlewareにcreateMiddlewareで作成したmiddlewareを入れる。*
start.tsを作成しない場合はCORSミドルウェアが自動で設定されるが、start.tsを作成する場合は書く必要がある。
createMiddleware
export const authMiddleware = createMiddleware({ type: 'request' }).server(
async ({ next, request }) => {
// /dashboard以下をmiddlewareでチェック。
const url = new URL(request.url)
if (
!url.pathname.startsWith('/dashboard')
) {
return next()
}
const headers = getRequestHeaders()
const session = await auth.api.getSession({ headers })
if (!session) {
throw redirect({
to: '/',
})
}
return next({
context: {
session,
},
})
},
)type:
request:すべてのリクエストの前に走る。
function:関数の実行前に走る。
context:
次の処理にmiddlewareで取得したUser情報等の値を渡せる。
使い方
type:requst
export const Route = createFileRoute('/')({
component: RouteComponent,
server:{
middleware:[authMiddleware]
},ルート定義ファイル、あるいはglobal middleware(createStart)に書く。
type:function
export const getFn = createServerFn({
method: 'GET',
}).
middleware([authFnMiddleware])
.handler(async ({ data, context }) => {
}関数ごとに使う。