Next.js: Custom middleware after nextjs RequestHandler

Created on 25 Jun 2019  路  5Comments  路  Source: vercel/next.js

Feature request

Related to https://github.com/zeit/next.js/issues/6290
We need to run some logic after every route is handled.

Is your feature request related to a problem? Please describe.

We have a Prometheus client where we report performance metrics.
"handleRequest" does not call next and therefore our code is never invoked.

Custom Server Example

  // Runs before each requests
  server.use((req, res, next) => {
    res.locals.startEpoch = Date.now();
    next();
  });

  server.get("/", (req, res) => {
    return handleRequest(req, res, "/", req.query);
  });

  server.get("*", (req, res) => {
    return handleRequest(req, res);
  });

  // Runs after each requests - **THIS NEVER GETS INVOKED**
  server.use((req, res, next) => {
    const responseTimeInMs = Date.now() - res.locals.startEpoch;
    httpRequestDurationMicroseconds
      .labels(req.method, req.route.path, res.statusCode)
      .observe(responseTimeInMs);

    next();
  });

Describe the solution you'd like

handleRequest should call next
This would solve https://github.com/zeit/next.js/issues/7499 as well allowing people to add middlewares

Describe alternatives you've considered

I've not found a workaround

All 5 comments

Maybe, instead of a middleware, you can use the res.on('finish') event for this in your main request handler?

Next.js does not provide a middleware, it ends the request when you call handle. So like @janpot is saying you'll probably want to add on finish or similar.

@timneutkens could you expand why adding a Middleware concept would be a bad idea for this framework ?

@timneutkens NextJS is great, I think it could be Amazing if I knew what is the correct way to open a dialogue about features such as middlewares or at least understanding why it does not fit the ZEIT's vision.

In the meantime, this is how to work around it as @Janpot kindly advised.

const handle = app.getRequestHandler();
const metricsInterval = Prometheus.collectDefaultMetrics();
const httpRequestDurationMicroseconds = new Prometheus.Histogram({
  name: "http_request_duration_ms",
  help: "Duration of HTTP requests in ms",
  labelNames: ["method", "route", "code"],
  buckets: [0.1, 5, 15, 50, 100, 200, 300, 400, 500] // buckets for response time from 0.1ms to 500ms
});

const onFinish = (req, res) => {
  res.on("finish", () => {
    const responseTimeInMs = Date.now() - res.locals.startEpoch;
    httpRequestDurationMicroseconds
      .labels(req.method, req.route.path, res.statusCode)
      .observe(responseTimeInMs);
  });

// Runs before each requests
  server.use((req, res, next) => {
    res.locals.startEpoch = Date.now();
    next();
  });

  server.get("/_next/*", (req, res) => {
    onFinish(req, res);
    /* serving _next static content using next.js handler */
    handle(req, res);
  });

  server.get("/", (req, res) => {
    onFinish(req, res);
    return handle(req, res, "/", req.query);
  });

  server.get("/dashboard", (req, res) => {
    onFinish(req, res);
    return handle(req, res, "/dashboard", req.query);
  });
};

There already was an open RFC before this issue was posted: https://github.com/zeit/next.js/issues/7208

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jesselee34 picture jesselee34  路  3Comments

rauchg picture rauchg  路  3Comments

pie6k picture pie6k  路  3Comments

knipferrc picture knipferrc  路  3Comments

ghost picture ghost  路  3Comments