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つの理由を解消するために、それぞれの正規表現のパターンを分解して解説して行きます。
/((?!.*\\..*|_next).*)
の解説
配列1:
/((?!.*\\..*|_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:この正規表現は、単純なルートパスにマッチします。
/(api|trpc)(.*)
の解説
配列3:この正規表現は、/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