Why you can't use variables in middleware's matcher

🌐

This post has been translated by DeepL . Please let us know if there are any mistranslations!

Problem

On certain pages, JavaScript would not execute and the screen would crash. Interestingly, the issue only occurred on the /signup page out of several paths defined in the middleware matcher.

Image.png

export const config = {
  matcher: [
    '/',
    '/account',
    Path.ITEMS, // Use variables here!
    '/blog/:path*',
    '/user/:path*',
    '/signin',
    '/signup',
    // ...
  ]
};

Cause

Explanation from the official Next.js documentation

According to the official Next.js documentation, the values of the middleware matcher must be statically resolvable at build time, and dynamic values such as variables are ignored.

Initially, I thought that since I declared Path.ITEMS as constright above config, I shouldn't have a problem. But in reality, I had a problem, and I analyzed the source code of Next.js to find out why.

Analyzing config in Next.js

Below is some of the actual code of Next.js, and the process of analyzing the configuration of SSG or middleware is as follows

export async function getPageStaticInfo(params: {
  nextConfig: Partial<NextConfig>
}): Promise<PageStaticInfo> {
  const fileContent = (await tryToReadFile(pageFilePath, !isDev)) || ''
  if (/runtime|getStaticProps|getServerSideProps|matcher/.test(fileContent)) {
    const swcAST = await parseModule(pageFilePath, fileContent)
    const config = tryToExtractExportedConstValue(swcAST, 'config') || {}
    // ...
  }
}

The important points in this code are

  1. It reads the configuration file first
  2. Finds a variable namedconfig
  3. Creates and parses an Abstract Syntax Tree (AST)

Looking more closely, Next.js only allows const declarations

export function extractExportedConstValue(
  module: Module,
  exportedName: string
): any {
  for (const moduleItem of module.body) {
    if (!isExportDeclaration(moduleItem)) continue;

    const declaration = moduleItem.declaration
    if (!isVariableDeclaration(declaration)) continue;

    if (declaration.kind !== 'const') continue;
    // ...
  }
}

This code shows that values that are not declared as const arenot extracted.

SWC's AST and static analysis

Next.js uses the Speedy Web Compiler (SWC) to parse your code and generate ASTs. During this process, settings that use variables are difficult to statically analyze, making them difficult to optimize at build time.

Example of a correct configuration that can be statically analyzed:

// βœ… Good example
export const config = {
  matcher: [
    '/',
    '/account',
    '/items', // use direct string
    '/blog/:path*',
  ]
};

Questions remaining

We haven't figured out why the /signup page was broken while the other pages worked fine. It probably has something to do with a quirk in Next.js' build process or code splitting.

Conclusion

You need to be careful when using variables in the Next.js middleware matcher. You should use direct strings for build time optimization and static analysis.