🦞

Clerkのmiddlewareのmatcherの正規表現を理解する

2023/08/13に公開

はじめに

Clerk の middleware の matcher で記載されている正規表現を解説します。

middleware.ts
export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

https://clerk.com/docs/nextjs/middleware

結論

Clerk の matcher は以下のようなパスがマッチします。

マッチするパス 説明
/ から始まり . を含まない、かつ、/_next から始まらない任意のパス .css, .js, .png などの拡張子を持つ静的ファイルを含まない、かつ、Next.js のビルドアウトプットが含まれる _next ディレクトリは含まない
/ のルートパス / と完全一致と
/api あるいは /trpc から始まる任意のパス APIルートあるいは、trpcを利用したルートを対象とすることと
middleware.ts
export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

詳細説明

Clerk の matcher はパッと見正規表現が複雑で、理解するのが難しいです。

middleware.ts
import { authMiddleware } from "@clerk/nextjs";

export default authMiddleware();

export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

https://clerk.com/docs/nextjs/middleware

matcher は、配列を用いて以下の 3 つのパスのパターンを定義しています。

  • /((?!.*\\..*|_next).*)
  • /
  • /(api|trpc)(.*)

理解が難しいのは、以下の2つの理由だと推測します。

  • 記号の意味がわからない。
  • 意味が分かっていても、適応される順序がわからない。

本記事では、上記の2つの理由を解消するために、それぞれの正規表現のパターンを分解して解説して行きます。

配列1:/((?!.*\\..*|_next).*) の解説


/((?!.*\\..*|_next).*) は、/から始まり.を含まない、かつ、/_nextから始まらない任意の文字列にマッチします。別の言葉でいうと、.css, .js, .png などの拡張子を持つ静的ファイルを含まない、かつ、Next.js のビルドアウトプットが含まれる _next ディレクトリは含まないパスにマッチします。

この正規表現を読み解くのは難易度が高いので、1 つずつ分解していきます。

.*

まず、.* は任意の文字列を表しています。より具体的には、.* は「任意の文字が 0 回以上続く」ことを意味します。

\\.

\\. は単純に、リテラルのピリオドを表しています。

正規表現において、ピリオド(.)は特殊な文字で、任意の一文字にマッチするワイルドカードとして機能します。ピリオド自体をリテラル文字としてマッチさせたい場合、通常はバックスラッシュ(\)を使ってエスケープします。したがって、通常の正規表現では、リテラルのピリオドを表すために \. と記述します。

しかし、JavaScript などのプログラミング言語では、文字列リテラルの中でバックスラッシュ自体もエスケープする必要があります。このため、文字列内で正規表現を書く際には、バックスラッシュを 2 回使用して \\. と記述する必要があるのです。

.*\\.

.*\\. は、. で終わる文字列とマッチします。

  • .* は任意の文字が 0 回以上続く。
  • \\. はリテラルのピリオド(.)を表す。

.*\\..*

.*\\..* は、. が含まれる文字列とマッチします。

  • .* は任意の文字が 0 回以上続く。
  • \\. はリテラルのピリオド(.)を表す。
  • .* は任意の文字が 0 回以上続く。

_next

_next は、リテラルの文字列 _next と一致することを意味します。

.*\\..*|_next

続いて、.*\\..*|_next は、.*\\..*、あるいは、_next のどちらかにマッチします。

| の OR 演算子で、左側のパターン(.*\\..*)または右側のパターン(_next)のいずれかにマッチする場合に全体としてマッチします。

?!.*\\..*|_next

?! を理解するには、「否定先読み」を理解する必要があります。「否定先読み」とは以下のようなものでパターンマッチさせたくない表現を ?! の後に記述します。例として正規表現が iPad(?!Pro) とします。


?!.*\\..*|_next は、.*\\..*|_next にマッチしない文字列にマッチします。
つまり、. を含む文字列、あるいは、_next という文字列以外の文字列にマッチします。

ここで、?!| の処理される順番について混乱しますが、?! は、.*\\..*|_next にかかります。そして、| は、.*\\..*|_next にかかります。

(?!.*\\..*|_next).*

(?!.*\\..*|_next).* は、.*\\..*|_next にマッチしない文字列かつ、その後任意の文字列が続く、文字列にマッチします。

/((?!.*\\..*|_next).*)


/((?!.*\\..*|_next).*) は、/ から始まる (?!.*\\..*|_next).* にマッチする文字列にマッチします。

配列2:/ の解説

この正規表現は、単純なルートパスにマッチします。

配列3:/(api|trpc)(.*) の解説

この正規表現は、/api または /trpc で始まる文字列にマッチします。| は OR 演算子で、左側のパターン(この場合は /api)または右側のパターン(この場合は /trpc)のいずれかにマッチする場合に全体としてマッチします。

最後に

Clerk の matcher は以下のようなパスがマッチします。

  • /((?!.*\\..*|_next).*) は、このパートはスラッシュで始まり、ピリオドが含まれず、_next という文字列そのものでない文字列にマッチします。
  • / は、これはルートパス(/)のみにマッチします。
  • /(api|trpc)(.*) は、このパートは /api または /trpc で始まり、その後に任意の文字列が続くパスにマッチします。
マッチするパス 説明
/ から始まり . を含まない、かつ、/_next から始まらない任意のパス .css, .js, .png などの拡張子を持つ静的ファイルを含まない、かつ、Next.js のビルドアウトプットが含まれる _next ディレクトリは含まないということ
/ のルートパス 完全一致で / を対象とすること
/api あるいは /trpc から始まる任意のパス APIルートあるいは、trpcを利用したルートを対象とすることと

まとめ

  • Clerk の middleware の matcher を解説しました。
  • 個人的には、?!| の処理の順序が混乱しました。

Discussion