←back to thread

Next.js is infuriating

(blog.meca.sh)
1033 points Bogdanp | 6 comments | | HN request time: 1.262s | source | bottom
Show context
Rauchg ◴[] No.45100460[source]
Heard and appreciate the feedback. We’re well aware of the DX papercuts in Middleware. With 15.5 we made a big step in supporting Node runtime[1] which addresses a slew of issues people have reported over time.

If I went back in time, I would have called it Routing Middleware or Routing Handler. A specific hook to intercept during the routing phase, which can be delivered to the CDN edge for specialized providers. It’s also a somewhat advanced escape hatch.

Since OP mentions logging, it’s worth noting that for instrumentation and observability we’ve embraced OpenTelemetry and have an instrumentation.ts convention[2]

[1] https://nextjs.org/blog/next-15-5#nodejs-middleware-stable

[2] https://nextjs.org/docs/app/api-reference/file-conventions/i...

replies(8): >>45100602 #>>45100630 #>>45100658 #>>45100894 #>>45101395 #>>45101475 #>>45104120 #>>45108837 #
1. rozumbrada ◴[] No.45100658[source]
If you finally decided to support proper server-side middleware, why is there still a limitation for only one middleware function and not a chain of middleewares as every other sane server implementation offers?
replies(1): >>45100760 #
2. bestest ◴[] No.45100760[source]
Consider middleware.ts as a root middleware. Nothing is stopping you from creating your own chain (which is trivial) in there. I mean, that would eventually work the same if nextjs implemented that feature — there would be a root somewhere.
replies(1): >>45100990 #
3. rs186 ◴[] No.45100990[source]
That doesn't answer parent's question.

People expect "middleware" to mean a certain thing and work a certain way.

replies(1): >>45101085 #
4. bestest ◴[] No.45101085{3}[source]

  middleware = fn(req) → next(req).
express/koa give you the use() chain. next.js gives you one root, but nothing stops you from chaining yourself. same semantics, just manual wiring.

  type mw = (req: Request, next: () => Response) => Response;
  
  const logger: mw = (req, next) => {
  console.log(req.url);
  return next();
};

  const auth: mw = (req, next) => {
    if (!req.headers.get("x-auth")) return new   Response("forbidden", { status: 403 });
    return next();
  };
  
  function chain(mws: mw[]) {
    return (req: Request) =>
      mws.reduceRight((next, mw) => () => mw(req, next), () => new Response("ok"))();
  }
  
  export function middleware(req: Request) {
    return chain([logger, auth])(req);
  }
root is given, chain is trivial. that’s middleware.
replies(1): >>45101466 #
5. rafaelmn ◴[] No.45101466{4}[source]
Nothing trivial about that implementation in my mind - need to keep track of where middleware is registered, reduceRight is non obvious.

I expect these things to be standardized by the framework and all the sharp edges filed off - thats why I go to a framework in the first place.

replies(1): >>45101578 #
6. foldr ◴[] No.45101578{5}[source]
The reduceRight is just a bit of cute FP code golf. All it’s saying is that chaining an empty list of middleware yields an ‘OK’ response, and that the first middleware is passed a function which, when called, executes the remaining middleware chain, and so on. It would be obvious enough if written out as a for loop, or via direct recursion.

(My username has never been more appropriate!)