Weird behavior with Next.js (help me understand why it happens)

So, Next.js has a file called middleware.js, in which you can define middleware to run on certain routes. See this link for more information: Advanced Features: Middleware | Next.js

To test it out, I created the following middleware.res file (which generates a .bs.js file):

let config = {
  "matcher": "/a/:path*",
}

let middleware = req => {
  Js.log(req["url"])
}

This, in turn, generates the following JS output:

// Generated by ReScript, PLEASE EDIT WITH CARE


var config = {
  matcher: "/a/:path*"
};

function middleware(req) {
  console.log(req.url);
  
}

export {
  config ,
  middleware ,
  
}
/* No side effect */

Running the Next.js app gives this:

http://localhost:3000/_next/static/development/_buildManifest.js?ts=1660630550290
http://localhost:3000/_next/static/development/_ssgManifest.js?ts=1660630550290
http://localhost:3000/_next/static/chunks/pages/a/docs.js?ts=1660630550290
http://localhost:3000/_next/static/chunks/react-refresh.js?ts=1660630550290
http://localhost:3000/a/docs

As you can see, the matcher export is ignored. It’s running on all routes. Why!?

If I change the .bs.js file to the following, then everything works (meaning it is only run for /a/ routes):

// Generated by ReScript, PLEASE EDIT WITH CARE

export const config = {
  matcher: "/a/:path*",
}

function middleware(req) {
  console.log(req.url)
}

export { middleware }
/* No side effect */

I’ve changed var to conts for config, and I’ve moved the export for config from the bottom export statement. Changing const back to var breaks everything again.

I did not know there was a semantic difference between export const and export var. Why is this happening?

Eh, this has to be some internal Next.js magic causing the issue. I’m not sure it’s even importing it, it might be reading it and parsing it, looking for export const or something…

1 Like

Correct. From the docs:

Note: The matcher values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored.

1 Like

Looks like the only way is to manually reexport from src/SomeMiddleware.res files to pages/middleware.js

For your config you could just

%%raw(`
export const config = {
  matcher: "/a/:path*",
}
`)

It may not be the cleanest solution but it co-locates your middleware config and you’re not missing out on type-safety for this very simple config object.

2 Likes