Passing variables from middleware to page in Next.js 12 new middleware api

Viewed 6141

Background to the Question

Vercel recently released their biggest update ever to Next.js. Next.js blog. They introduced a lot of new features but my favorite is Middleware which:

"enables you to use code over configuration. This gives you full flexibility in Next.js because you can run code before a request is completed. Based on the user's incoming request, you can modify the response by rewriting, redirecting, adding headers, or even streaming HTML."

The Question

The following structure is used in this question.

- /pages
    index.js
    signin.js
    - /app
      _middleware.js # Will run before everything inside /app folder
      index.js

The two important files here are /app/_middleware.js and /app/index.js.

// /app/_middleware.js

import { NextResponse } from 'next/server';

export function middleware(req, event) {
  const res = { isSignedIn: true, session: { firstName: 'something', lastName: 'else' } }; // This "simulates" a response from an auth provider
  if (res.isSignedIn) {

    // Continue to /app/index.js
    return NextResponse.next();
  } else {

    // Redirect user
    return NextResponse.redirect('/signin');
  }
}
// /app/index.js

export default function Home() {
  return (
    <div>
      <h1>Authenticated!</h1>
      
      // session.firstName needs to be passed to this file from middleware
      <p>Hello, { session.firstName }</p>
    </div>
  );
}

In this example /app/index.js needs access to the res.session JSON data. Is it possible to pass it in the NextResponse.next() function or do you need to do something else?

In express you can do res.locals.session = res.session

3 Answers

According to the examples (look specifically at /pages/_middleware.ts and /lib/auth.ts) it looks like the canonical way to do this would be to set your authentication via a cookie.

In your middleware function, that would look like:

// /app/_middleware.js

import { NextResponse } from 'next/server';

export function middleware(req, event) {
  const res = { isSignedIn: true, session: { firstName: 'something', lastName: 'else' } }; // This "simulates" a response from an auth provider
  if (res.isSignedIn) {

    // Continue to /app/index.js
    return NextResponse.next().cookie("cookie_key", "cookie_value"); // <--- SET COOKIE
  } else {

    // Redirect user
    return NextResponse.redirect('/signin');
  }
}

Only weird solution is to inject your custom object into req.body because next.js v12 middleware doesn't allow altering the NextApiRequest

export const middleware = async (req: NextApiRequest) => {
  //   return new Response("Hello, world!");
  req.body = { ...req.body, foo: "bar" };
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  await middleware(req);
  // now req.body.foo=='bar'
}

They do however explain how you can extend middleware here, but the example given (copied below) isn't meaningful enough because it doesnt show how withFoo() is implemented

import { NextApiRequest, NextApiResponse } from 'next'
import { withFoo } from 'external-lib-foo'

type NextApiRequestWithFoo = NextApiRequest & {
  foo: (bar: string) => void
}

const handler = (req: NextApiRequestWithFoo, res: NextApiResponse) => {
  req.foo('bar') // we can now use `req.foo` without type errors
  res.end('ok')
}

export default withFoo(handler)

I assumed based on the above, withFoo.ts should be like this. But still wasn't successful in accessing request.Foo()

import { NextApiHandler, NextApiRequest } from "next";


export const withFoo = (handler: NextApiHandler) => {
  //do stuff

};

Maybe someone can chip in?

There's a another way but just like using cookie to achieve this. Just pass you data through headers.

// middleware.ts

async function middleware(request: NextRequest) {
  const response = NextResponse.next();
  response.headers.set('X-HEADERS', 'some-value-to-pass');
  return response;
}
// _app.ts

function MyApp({ data }) {
  // you can access your data here
  <div>{data}</div>
}

MyApp.getInitialProps = ({ ctx }) => {
  const data = ctx.res.getHeader('X-USER');
  ctx.res.removeHeader('X-USER');
  return { data };
};
Related