netlify/next-on-netlify

Redirect for preview mode

nikshenoy opened this issue ยท 12 comments

The Next.js samples for preview mode involve doing a redirection from an api route. The code is something like:

  res.setPreviewData({
    maxAge: 60 * 60 // The preview mode cookies expire in 1 hour
  })

  res.writeHead(307, { Location: `/${req.query.contentType}/${req.query.slug}` })

I can do this from next run, but if I try this on Netlify it seems to strip the location header on the 307 redirect. Is this something to do with the compat call or am I doing something wrong?

Hey @nikshenoy,

I've been digging into this issue and there are two parts to the answer:

  1. The location header being stripped: In the next-on-netlify master version, headers are set as multiValueHeaders โ€” turns out these are not supported by AWS, but not by Netlify. It's a long-standing issue: https://community.netlify.com/t/go-lambdas-differences-with-aws-lambdas-prevents-me-from-using-algnhsa/1249

    I am able to work around that limitation by converting multiValueHeaders to plain headers. The code is already written and will be released with the release version of next-on-netlify. So the redirect will now very much work! See also: #9 (comment)

  2. Cookies not being set: When you call res.setPreviewData({}), it attempts to set two cookies on the response: __prerender_bypass and __next_preview_data. Netlify only supports setting one cookie per function call. It's a bug on Netlify's end and a long-standing issue: https://community.netlify.com/t/multiple-set-cookie-headers-cause-netlify-lambda-to-throw-an-error/975

    So the second cookie is quietly dropped when you invoke the Netlify Function. NextJS expects both cookies to be present, so it does not activate preview mode. Unfortunately, I do not have a work-around for this issue.

Sorry about that :| If you want, I'd be happy to help brainstorm alternative approaches to your situation!

โ€” Finn

@calavera from Netlify just responded to an issue I filed with Netlify regarding multiValueHeaders:

Thanks for opening this issue @FinnWoelm ! I think this issue is caused because we've not upgraded our internal type definitions for Lambda responses in a while. I'm going to transfer this issue to a private repository later today so we can triage it. I don't think it should be hard to fix. Since there is a thread in community, we'll send updates there.
From: https://github.com/netlify/cli/issues/923#issuecomment-637026749

Perhaps that means they will also look into adding support for setting multiple cookies on the same function response.

If not, we could think about a workaround. For example: Before the response is sent out, we could check for the presence of the two NextJS cookies and, if they exist, encode both into a single cookie called __next_preview_on_netlify. Whenever a Netlify Function is invoked, we can then check for that single cookie and decode it back into two. This happens before any NextJS code is invoked, so for all intents and purposes, NextJS will just see the requests as if it actually had the two cookies.

Let me know if this is something that you're interested in. I would love to hear more about your use case, as well, to make sure I'm not missing anything.

Hey @FinnWoelm, thanks so much for the quick (and thorough response). The multiValueHeaders fix sounds good. I think that will solve the problem (especially if Netlify fixes it, which is unclear given how long the original issue was open).

I guess you're correct that we can call Next.js's cookie setter, re-encode those cookies, and then decode them on the next-on-netlify side of that function. I'm willing to give that a shot as a workaround. Is the best way to have a generic header for this, like: Set-Cookie-Compressed so it doesn't conflict with the standard one? Then your code can just check that, decode it and add it to the normal cookies?

Hey @nikshenoy,

I just released next-on-netlify v2 ๐ŸŽ‰ ๐ŸŽ‰ It brings the promised support for setting headers in SSR pages and API endpoints (such as the Location header you were referencing!). You can see the full list of changes in the changelog.

You'll need to make some changes to your netlify.toml file for v2, both for deployment and for local preview:

# netlify.toml

[build]
  command   = "npm run build"
  functions = "out_functions"
  publish   = "out_publish"

[dev]
  functions = "out_functions"
  publish   = "out_publish"
  framework = "#static"

And you will want to adjust your .gitignore for previewing locally:

# .gitignore

# Files generated by next-on-netlify command
/out_publish/
/out_functions/
/404.html

Regarding the cookies work-around: I would actually implement this completely within next-on-netlify. I would check any outgoing responses for the presence of the two NextJS cookies and encode them if present. And I would check any incoming requests for the presence of the encoded cookie and decode it into the two NextJS components. So you, as a user of next-on-netlify, would not have to change your code in any way!

Before I do that, I want to make sure this would actually be helpful for your use case, though. For example, it's not possible to do cookie-based redirects with Netlify (at the moment; see this thread for ongoing work). So if you have certain paths pre-rendered as HTML, your redirects will still point to the pre-rendered pages, even if preview mode is turned on.

Preview mode will only affect pages that are SSR-ed, so any pages with getInitialProps, any pages with getServerSideProps, and any pages with getStaticProps with fallback: true as long as the path has not been defined in getStaticPaths.

Does that make sense? What do you think? Does that cover your use case?
- Finn

If you can implement it within the library, that sounds like a good solution. I see what you mean with regards to pages that are static rendered. I have two comments:

  1. The site I'm currently working on is all SSR right now; it's possible that we could move some things to SSG later, but it won't affect us in the short term. From a selfish perspective, it's not a big deal to me right now :-)

  2. If we were accessing non-SSR pages, maybe it's possible to have a special Netlify redirect for preview mode so that the preview API could redirect to /preview?url=<target_url> and then that would force the request to go to the Netlify Function and it could handle any remapping required.

Hey @nikshenoy,

That sounds like a good use case! I'll try to turn it around this weekend :)

Regarding the use of /preview?url=<target_url>. Hmm, I guess you could do something like that. Although you won't get the magic of preview mode applying to your entire website. I mean, I guess you could conditionally change all your NextJS <Link>s depending on whether preview mode is enabled... But let's just make Netlify to introduce cookie-based redirects?! ๐Ÿ˜‹ https://community.netlify.com/t/expanding-functionality-of-redirects/988/36

I'll keep you posted on the cookie encoding/decoding!
- Finn

Hey @nikshenoy,

I've got very exciting news: From what I can tell, Netlify just implemented support for multiValueHeaders and for setting multiple cookies from a Netlify Function. I just tested setting two cookies from a demo function and it worked.

I'm trying to find out right now if the support for multiValueHeaders and multiple cookies is stable (I'm assuming it is, but want to double check). If it is, then I'll update next-on-netlify to use multiValueHeaders again and preview mode should work without any workarounds! (Of course the caveats about preview mode not working on pre-rendered pages will still apply, until we get cookie-based redirects.)

Anyway, this is super exciting ๐Ÿ”ฅ and I'll let you know as soon as I hear something from Netlify!

Happy Sunday,
Finn

That sounds great. And you're right about the magic of cookies for preview mode. I keep forgetting that it keeps that mode on as you're navigating. Thanks for the update!

Hi @FinnWoelm Any news about adding multiValueHeaders ? Anything I can help with?

Hey @scopsy, thanks for following up on this! Good news: I actually finished writing the code yesterday ๐Ÿ˜ I just did not have time to push and release. Doing that right now; give me ~15 minutes :)

You are awesome! ๐Ÿ˜

Alright, we are live!! ๐ŸŽ‰ ๐Ÿ˜Š next-on-netlify now supports NextJS Preview Mode as well as multiValueHeaders!

Let me know if you run into any issues :)

PS: @scopsy, I saw your issue regarding redirects! I will look into it this weekend and see what we can do ๐Ÿ™ƒ