Yanonoblog!

こつこつと

Next.js - ミドルウェア関数

背景

実務ではReactを使っているのですがNext.jsのキャッチアップしておきたいと思ったので、その内容を整理したいと思います。

本記事では、Next.jsに関する情報をまとめています。

内容は易しめです。

前回の続きになります!

Next.js - SWR

ミドルウェア

Next.jsでは、APIルートのリクエスト/レスポンスに対してミドルウェアを適用することができます。

ミドルウェアは、リクエスト/レスポンスを調整するための関数であり、APIルートにリクエストが送信される前またはレスポンスが返される前に実行されます。

ミドルウェアは、クライアントからリクエストがあると、「ミドルウェアチェーン」と呼ばれる機能により次々とミドルウェアが呼び出されていきます。

すべてのミドルウェアが呼び出された後に、ページのコンポーネントが呼び出され表示が作成されるようになっています。

主な使用用途 - Next.jsにおけるミドルウェア

ミドルウェアを使用することで、APIルート関数に共通する処理や、リクエスト/レスポンスの調整を行うことができます。

例えば、認証処理やログ出力処理などが考えられます。

middleware.ts - ミドルウェアの定義

Next.jsでは、ミドルウェアAPIルートに適用するために、middlewaresディレクトリ、またはsrc配下などに**middlewares.ts**ファイルを作成し、その中にミドルウェア関数を定義します。 き

基本コード

処理の流れ

  1. クライアントからのリクエストがサーバーに到達すると、リクエストは最初のミドルウェア関数に渡されます。
  2. ミドルウェア関数はリクエストを処理し、必要に応じてレスポンスを生成します。
  3. ミドルウェア関数はレスポンスを次のミドルウェア関数に渡します。この時、前のミドルウェア関数で生成されたレスポンスが次の関数に渡されます。
  4. 最後のミドルウェア関数がレスポンスを生成し、クライアントに送信されます。

このように、ミドルウェアはチェーンのような形で連続的に実行され、リクエストとレスポンスを加工していくことができます。

NextRequestとNextResponse

ミドルウェア関数は、Next.jsのリクエストオブジェクトであるNextRequestを受け取り、処理後にレスポンス情報であるNextResponseオブジェクトを返すことが基本的な役割となります。

クライアントからのリクエストによってアプリケーションを実行したタイミングでミドルウェアをチェーンして呼び出し、リクエストとレスポンスが加工された上でコンポーネントを返す仕組みになっているからです。

例 - UserAgentを確認し、レスポンスに含めるミドルウェア関数

関数の中で、リクエストヘッダーからuser-agentを取得し、その中にgooglebot が含まれているかどうかを確認しています。

もし含まれていれば、Googlebot用のメッセージを含むレスポンスオブジェクトを返し、含まれていなければNextResponse.next()を呼び出して、次のミドルウェアまたはページの処理を継続します。

import { NextRequest, NextResponse } from 'next/server'

export default function middleware(req: NextRequest) {
  const userAgent = req.headers.get('user-agent')

  if (userAgent && userAgent.includes('googlebot')) {
    return new Response('User-agent is Googlebot')
  }

  return NextResponse.next()
}

実際のソース

こちらのサービスはGitHubソースを公開されているサービスです。

https://drift.maxleiter.com/

こちらのソースを覗いてみると**/src/middleware.ts**ミドルウェアウェア関数が定義されていることがわかります。

https://github.com/MaxLeiter/Drift/blob/refactor/src/middleware.ts

認証機能を実現するためのミドルウェアの設定が書かれており、このミドルウェアを使用することで、アクセス制限をかけたいページに対して認証機能を実装することができます。

withAuth関数でラップされたミドルウェア関数の中で、認証されたトークンの有無や、アクセスしようとしているページのパスなどをチェックして、認証されていない場合はログインページにリダイレクトするようになっています。

import { getToken } from "next-auth/jwt"
import { withAuth } from "next-auth/middleware"
import { NextResponse } from "next/server"

const PAGES_REQUIRE_AUTH = ["/new", "/settings", "/mine", "/admin"]

export default withAuth(
    async function middleware(req) {
        const token = await getToken({ req })

        const isAuthed = !!token
        const isAuthPage =
            req.nextUrl.pathname.startsWith("/signup") ||
            req.nextUrl.pathname.startsWith("/signin")

        const isPageRequireAuth = PAGES_REQUIRE_AUTH.includes(req.nextUrl.pathname)

        if (isAuthPage) {
            if (isAuthed) {
                return NextResponse.redirect(new URL("/new", req.url))
            }

            return null
        } else if (isPageRequireAuth && !isAuthed) {
            return NextResponse.redirect(new URL("/signin", req.url))
        }

        return NextResponse.next()
    },
    {
        callbacks: {
            async authorized() {
                // This is a work-around for handling redirect on auth pages.
                // We return true here so that the middleware function above
                // is always called.
                return true
            }
        }
    }
)

export const config = {
    matcher: [
        /*
        * Match all request paths except for the ones starting with:
        * - api (API routes)
        * - _next/static (static files)
        * - _next/image (image optimization files)
        * - favicon.ico (favicon file)
        */
        "/((?!api|_next/static|_next/image|favicon.ico).*)"
    ]
}

続く…

コメント

本記事の内容は以上になります!

続きのアウトプットも随時更新したいと思います。

間違いがありましたら修正いたしますので、ご指摘ください。

興味があれば他の記事も更新していきますので是非ご覧になってください♪


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky

参考