NextRequest
| 対応: | Next.js 13(2022) |
|---|
『Next.js』の NextRequest は、ミドルウェアやRoute Handlerで受け取るリクエストオブジェクトです。Web標準の Request クラスを継承しており、URL情報・クッキー・ヘッダーなどに簡単にアクセスできるメソッドやプロパティが追加されています。next/server からインポートして使用します。
構文
// NextRequest の基本的な使い方(ミドルウェア内)
import { NextRequest, NextResponse } from 'next/server';
// middleware 関数の引数として NextRequest を受け取ります
export function middleware(request) {
// request は NextRequest のインスタンスです
// URL情報へのアクセス
const pathname = request.nextUrl.pathname; // 現在のパス(例: /dashboard)
const search = request.nextUrl.search; // クエリ文字列(例: ?id=1)
// Cookieの取得
const token = request.cookies.get('token'); // 指定名のCookieを取得します
const allCookies = request.cookies.getAll(); // すべてのCookieを配列で取得します
// ヘッダーの取得
const ua = request.headers.get('user-agent'); // User-Agentヘッダーを取得します
const lang = request.headers.get('accept-language'); // 言語設定を取得します
// リクエストメソッドの確認
const method = request.method; // 'GET' / 'POST' など
// IPアドレスの取得(Vercel環境など)
const ip = request.ip;
return NextResponse.next();
}
主なプロパティとメソッド一覧
| プロパティ / メソッド | 概要 |
|---|---|
request.nextUrl | リクエストURLの情報を持つオブジェクトです。pathname・search・searchParams・origin などにアクセスできます。 |
request.nextUrl.pathname | リクエストのパス部分を文字列で返します(例:/dashboard/settings)。 |
request.nextUrl.searchParams | クエリパラメータを操作する URLSearchParams オブジェクトです。get() で個別の値を取得できます。 |
request.cookies.get(name) | 指定した名前のCookieを { name, value } 形式で返します。存在しない場合は undefined を返します。 |
request.cookies.getAll() | リクエストに含まれるすべてのCookieを配列で返します。 |
request.cookies.has(name) | 指定した名前のCookieが存在するかどうかを真偽値で返します。 |
request.headers.get(name) | 指定したヘッダーの値を文字列で返します。存在しない場合は null を返します。 |
request.method | HTTPメソッド名を大文字の文字列で返します(例:'GET'・'POST')。 |
request.url | リクエストの完全なURLを文字列で返します(例:https://example.com/dashboard?id=1)。 |
request.ip | クライアントのIPアドレスを返します。Vercelなど対応環境でのみ利用できます。 |
request.geo | クライアントの地理情報(国・都市・地域)を返します。Vercelなど対応環境でのみ利用できます。 |
request.body | リクエストボディを ReadableStream として返します。request.json() や request.text() で内容を取得できます。 |
サンプルコード
Cookieのトークンを確認して認証を行い、未認証のユーザーをログインページへリダイレクトするミドルウェアの例です。
// middleware.js
// NextRequest を使って認証状態を確認するミドルウェアです
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request) {
// request.nextUrl.pathname でリクエストのパスを取得します
const pathname = request.nextUrl.pathname;
// ログインページ自体へのアクセスはチェックをスキップします
if (pathname.startsWith('/login')) {
return NextResponse.next();
}
// request.cookies.get() で認証トークンを取得します
// Cookie名 'auth_token' はログイン時にサーバーが発行します
const tokenCookie = request.cookies.get('auth_token');
// Cookieが存在しない場合は未認証とみなします
if (!tokenCookie) {
// ログイン後に元のページへ戻れるよう、リダイレクト先をクエリパラメータに付与します
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('redirect', pathname);
// ログインページへリダイレクトします
return NextResponse.redirect(loginUrl);
}
// 認証済みの場合はリクエストをそのまま続行します
return NextResponse.next();
}
// /dashboard 以下のすべてのパスにミドルウェアを適用します
export const config = {
matcher: ['/dashboard/:path*'],
};
Route HandlerでNextRequestを使い、クエリパラメータとリクエストボディを取得する例です。
// app/api/users/route.js
// NextRequest を使ってクエリパラメータとリクエストボディを処理するRoute Handlerです
import { NextRequest, NextResponse } from 'next/server';
// GET /api/users?role=admin のようにクエリパラメータを受け取ります
export async function GET(request) {
// request.nextUrl.searchParams で URLSearchParams オブジェクトを取得します
const searchParams = request.nextUrl.searchParams;
// 'role' クエリパラメータの値を取得します(例: 'admin')
const role = searchParams.get('role');
// 'page' クエリパラメータを取得します(デフォルトは '1')
const page = searchParams.get('page') || '1';
// 取得したパラメータをもとにデータを返す例です
// 実際はデータベースへの問い合わせなどを行います
return NextResponse.json({
role: role,
page: parseInt(page, 10),
message: '指定された条件でユーザー一覧を取得しました',
});
}
// POST /api/users にJSONボディを送信してユーザーを作成します
export async function POST(request) {
// request.json() でリクエストボディをJSON形式でパースします
// Content-Type: application/json のリクエストに対応します
const body = await request.json();
// ボディから必要なフィールドを取り出します
const { name, email } = body;
// バリデーション:必須フィールドが存在するか確認します
if (!name || !email) {
return NextResponse.json(
{ error: '名前とメールアドレスは必須です' },
{ status: 400 }
);
}
// ユーザー作成処理(ここではサンプルとして固定値を返します)
return NextResponse.json(
{ id: 1, name: name, email: email, message: 'ユーザーを作成しました' },
{ status: 201 }
);
}
リクエストヘッダーの Accept-Language を読み取り、言語に応じたページへリダイレクトする例です。
// middleware.js
// Accept-Language ヘッダーをもとに適切な言語ページへリダイレクトします
import { NextRequest, NextResponse } from 'next/server';
// サポートする言語の一覧です
const LOCALES = ['ja', 'en', 'zh'];
// デフォルトの言語です(ヘッダーで判定できなかった場合に使用します)
const DEFAULT_LOCALE = 'ja';
export function middleware(request) {
const pathname = request.nextUrl.pathname;
// すでに言語プレフィックスが付いているパスはそのまま続行します
// 例: /en/about はスキップします
const hasLocalePrefix = LOCALES.some(function(locale) {
return pathname === '/' + locale || pathname.startsWith('/' + locale + '/');
});
if (hasLocalePrefix) {
return NextResponse.next();
}
// request.headers.get() で Accept-Language ヘッダーを取得します
// 例: "ja,en-US;q=0.9,en;q=0.8"
const acceptLanguage = request.headers.get('accept-language') || '';
// 最優先の言語コードを抽出します("ja-JP" → "ja")
const preferredLang = acceptLanguage.split(',')[0].split('-')[0].toLowerCase();
// サポート対象か確認し、対象外ならデフォルト言語を使います
const locale = LOCALES.includes(preferredLang) ? preferredLang : DEFAULT_LOCALE;
// 言語プレフィックスを付けたURLへリダイレクトします
const redirectUrl = new URL('/' + locale + pathname, request.url);
return NextResponse.redirect(redirectUrl);
}
export const config = {
// 静的ファイルと Next.js 内部パスを除いたリクエストにのみ適用します
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
概要
NextRequest は Web標準の Request クラスを継承したクラスで、next/server パッケージからインポートして使用します。主にミドルウェア(middleware.js)とRoute Handler(route.js)の引数として受け取ります。
request.nextUrl は標準の URL オブジェクトを拡張したもので、pathname・searchParams・origin などにアクセスできます。リダイレクト先のURLを生成する際は new URL('/path', request.url) と組み合わせて使うのが一般的です。
request.cookies は専用の RequestCookies クラスのインスタンスで、get(name)・getAll()・has(name) といったメソッドでCookieを操作できます。request.headers は Headers インターフェイスに準拠しており、get(name) でヘッダー値を取得できます。
request.ip や request.geo はVercelなど特定のデプロイ環境でのみ利用できるプロパティです。セルフホスト環境では undefined となるため、使用する場合は存在確認を行うことをお勧めします。
ミドルウェアの書き方については middleware.js、Route Handlerの定義については route.js もあわせてご参照ください。
よくあるミス
よくあるミス1: NextRequest と Web 標準 Request を混同してメソッドが見つからない
NextRequest は Web 標準の Request クラスを継承していますが、nextUrl・cookies.get()・ip・geo などのプロパティは NextRequest 固有のものです。Route Handler の引数を Request 型として扱うと、これらのプロパティにアクセスできません。
ng_example.jsx
// app/api/profile/route.js
// 引数を標準の Request として扱っている例です
import { NextResponse } from 'next/server';
export async function GET(request) {
// request.nextUrl は NextRequest 固有のプロパティです
// 標準の Request オブジェクトには存在しません
const pathname = request.nextUrl.pathname; // エラーになる場合があります
// request.cookies.get() も NextRequest 固有のメソッドです
const token = request.cookies.get('token'); // エラーになる場合があります
return NextResponse.json({ pathname, token });
}
TypeError: Cannot read properties of undefined (reading 'pathname')
NextRequest を next/server からインポートして使用します。ミドルウェアと Route Handler の引数はいずれも NextRequest のインスタンスです。
ok_example.jsx
// app/api/profile/route.js
// NextRequest をインポートして使用する例です
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request) {
// request は NextRequest のインスタンスです
const pathname = request.nextUrl.pathname;
const token = request.cookies.get('token');
return NextResponse.json({ pathname, token });
}
よくあるミス2: ミドルウェアで body を読もうとして空になる
Edge Runtime 上で動作するミドルウェアでは、request.body(ReadableStream)を読み取ることができません。request.json() や request.text() を呼び出すと、エラーになるか空の結果が返ります。リクエストボディの読み取りはミドルウェアではなく Route Handler(route.js)で行う必要があります。
ng_example.jsx
// middleware.js
// ミドルウェアでリクエストボディを読もうとしている例です
import { NextResponse } from 'next/server';
export async function middleware(request) {
// Edge Runtime ではボディの読み取りができません
const body = await request.json(); // エラーまたは空になります
const { userId } = body;
return NextResponse.next();
}
Error: Request body is not available in middleware. Body has already been consumed or is not readable in the Edge Runtime.
ミドルウェアではボディを読み取れません。認証チェックなどにボディの内容が必要な場合は、Route Handler 内でボディを読み取り、必要な情報をヘッダーやクッキーに設定してから次の処理へ渡す設計を検討します。
ok_example.jsx
// app/api/users/route.js
// Route Handler でリクエストボディを読み取る例です
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request) {
// Route Handler ではボディを正しく読み取れます
const body = await request.json();
const { name, email } = body;
if (!name || !email) {
return NextResponse.json({ error: 'name and email are required' }, { status: 400 });
}
return NextResponse.json({ name, email }, { status: 201 });
}
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。