Clerkのmiddlewareのmatcherの正規表現を理解する
はじめに
Clerk の middleware の matcher で記載されている正規表現を解説します。
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
結論
Clerk の matcher は以下のようなパスがマッチします。
| マッチするパス | 説明 |
|---|---|
/ から始まり . を含まない、かつ、/_next から始まらない任意のパス |
.css, .js, .png などの拡張子を持つ静的ファイルを含まない、かつ、Next.js のビルドアウトプットが含まれる _next ディレクトリは含まない |
/ のルートパス |
/ と完全一致と |
/api あるいは /trpc から始まる任意のパス |
APIルートあるいは、trpcを利用したルートを対象とすることと |
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
詳細説明
Clerk の matcher はパッと見正規表現が複雑で、理解するのが難しいです。
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware();
export const config = {
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
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