3

I am using next-connect to apply some express middleware to my next.js application. While trying to add csrf middleware, I encountered the following error:

res.redirect is not a function

This error only appears when I applied the middleware in getServerSideProps, it works fine in my API endpoints.
This works for example:

// in /pages/api/test.ts
nc()
 .use(db)
 .use(initializedSession)
 .use(csrfProtection)
 .use(initializedPassport)
 .use(initializedPassportSession)
 .post(
    passport.authenticate("local", {failureRedirect: "/login"}),
    (req: NextApiRequest, res: NextApiResponse, next) => {
       return res.redirect("/");
    }
 )

Does work, but applying the same in getServerSideProps for a react page:

// In /pages/login.tsx
export const getServerSideProps: GetServerSideProps = async ({req, res}) => {
  await nc()
    .use(db)
    .use(initializedSession)
    .use(csrfProtection)
    .use(initializedPassport)
    .use(initializedPassportSession)
  .run(req, res);
  return {
    props: {
      csrfToken: req.csrfToken(),
    },
  };
};

Does not work and results in res not having a redirect method.

I'm not quite sure about this behavior, but I'm sure I'm missing something silly. Any one an idea?

Bram Vanbilsen
  • 5,763
  • 12
  • 51
  • 84
  • Aha, that makes total sense! Is there any reason why this is the case and is there a way to extend the `http.ServerResponse` in `getServerSideProps`? – Bram Vanbilsen Aug 01 '21 at 18:31
  • @brc-dd Thanks, I was taking a look at that as well! I'll try to use it to convert the response. Seems easy enough, but I've got the feeling that there will be some gotchas, otherwise the next.js team would have probably implemented it themselves. Either way, your response was the answer I was looking for! I can't make it as correct because it is too short. If you want some of those Stackoverflow points, feel free to write something a little longer and I'll mark your response as the solution :) – Bram Vanbilsen Aug 01 '21 at 18:59

2 Answers2

5

The res object of getServerSideProps is of type http.ServerResponse, and has no method named redirect. Whereas in APIs, the res object is NextApiResponse having some additional helpers like the redirect method. That's the reason your code was working fine when it was there inside an API logic.

But Next.js recommends this:

Note: You should not use fetch() to call an API route in getServerSideProps. Instead, directly import the logic used inside your API route. You may need to slightly refactor your code for this approach.

Fetching from an external API is fine!

So, your approach is quite fine at its core, but for it to work, you'll have "slightly refactor" your code. Now, Next.js internally uses an apiResolver middleware. You can try using it or your custom resolver.

Note that internal APIs are susceptible to change, hence it is generally not recommend to directly use them in your production code. But you can easily clone the logic from the linked file to your own shared library.

To handle the response part, you can do something like this:

import type { GetServerSideProps, NextApiRequest, NextApiResponse } from 'next';

import {
  redirect,
  sendData,
  sendJson,
  sendStatusCode,
} from 'next/dist/next-server/server/api-utils'; // or from 'custom-api-resolver'

// component logic here ...

const getServerSideProps: GetServerSideProps = async ({
  req: apiReq,
  res: apiRes,
}) => {
  const req = apiReq as NextApiRequest;
  const res = apiRes as NextApiResponse;

  res.status = (statusCode) => sendStatusCode(res, statusCode);
  res.send = (data) => sendData(req, res, data);
  res.json = (data) => sendJson(res, data);
  res.redirect = (statusOrUrl: number | string, url?: string) =>
    redirect(res, statusOrUrl, url);

  // api logic here...

  return { props: {} };
};

export { getServerSideProps };

References:

I have not thoroughly tested this, but for the basics this shoudld work fine.

brc-dd
  • 10,788
  • 3
  • 47
  • 67
  • Here is an updated link: https://github.com/vercel/next.js/blob/72f5c93aaded6b5310e9d8796857768c5432b4f2/packages/next/server/api-utils/node.ts#L161 (the current commit from the main 'canary' branch https://github.com/vercel/next.js/blob/canary/packages/next/server/api-utils/node.ts) Unfortunately `sendData` and `sendJson` functions are no longer `export`ed. – Ryan May 11 '22 at 21:52
  • I'd love if you could please take a look at https://stackoverflow.com/q/72206121/470749 because maybe you know how to solve it nowadays too. Thanks. If I find the answer, I'll post there. – Ryan May 11 '22 at 22:00
  • @Ryan Then I guess you need to copy those functions to your file. Or find some some package that already does that for you. – brc-dd May 12 '22 at 03:52
1

getServerSideProps has redirect return value

https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#redirect

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    }
  }

  return {
    props: {}, // will be passed to the page component as props
  }
}
Adrian Bienias
  • 829
  • 10
  • 16