Initial Server Side Rendering Fails, Tries To Make Request to Current Server
See original GitHub issueIntended outcome:
I’m building a NextJS app that uses apollo-client to fetch data during Server-side rendering, with credentials included.
Actual outcome:
Any time I try to include CORS credentials to include cookies, the initial request will fail on the server side.
On initial load, you see that several components start in the “loading” state, even though they’re supposed to be rendered server-side. They do eventually populate on the client, but in my logs I’m getting this error:
[Network error]: SyntaxError: Unexpected token N in JSON at position 0
Which I know means that I’m getting HTML or an error back when apollo-client expects JSON.
But the reason this is happening is because it looks like apollo-client is trying to make a request to the same server that I’m doing SSR on, not the endpoint that I’ve specified in my config:
at=info method=POST path="/" host=advanced-react-frontend.herokuapp.com request_id=d406a7fe-4fc4-4dc0-89fd-9ab9076b1608 fwd="71.78.33.134,54.147.22.164" dyno=web.1 connect=0ms service=4ms status=501 bytes=123 protocol=https
Here’s a snippet of my config as well:
https://github.com/mattfwood/next-apollo-issue/blob/master/lib/withData.js
return new ApolloClient({
uri: 'https://advanced-react-backend.herokuapp.com',
request: operation => {
operation.setContext({
fetchOptions: {
credentials: 'include',
},
headers,
});
},
});
How to reproduce the issue:
Here’s a live demo of the issue: https://advanced-react-frontend.herokuapp.com/
And here’s the repo: https://github.com/mattfwood/next-apollo-issue
I’ve been trying to fix this for more than 10 hours. Here are the things I’ve identified and that made it so difficult to debug:
- This only happens when my frontend and backend are both on real servers. When I run my frontend locally, it always successfully queries my (local) server while doing server-side rendering.
- This made me think that it might’ve been a CORS issue, but I’ve triple checked and I am getting no request to my backend server before the page initially renders. The issue is because apollo-client is trying to query the current server (my NextJS server) first, which doesn’t have a route for a POST to
/
, so it returns a 501. In fact, if I useapollo-link-error
to force Apollo to throw an error if a query fails, I get absolute no requests logged on my server. - If I turn CORS completely off on both my frontend and backend, it works as intended. But obviously since I’m trying to use cookies to store user session, this isn’t an option.
- I’ve also tried copying this example apollo config exactly, and it still has the same issues.
Any help would be greatly appreciated. The entire apollo library is incredibly well-designed and I’ve been trying to dig into the source code to figure out why this might happen, but so far I haven’t had any luck.
Versions
System: OS: macOS 10.14.1 Binaries: Node: 11.1.0 - /usr/local/bin/node Yarn: 1.12.1 - /usr/local/bin/yarn npm: 6.4.1 - /usr/local/bin/npm Browsers: Chrome: 70.0.3538.110 Safari: 12.0.1 npmPackages: apollo-boost: ^0.1.16 => 0.1.22 apollo-client: ^2.4.2 => 2.4.7 next-with-apollo: ^3.1.3 => 3.3.0 react-apollo: ^2.2.1 => 2.3.2
Issue Analytics
- State:
- Created 5 years ago
- Comments:17
Top GitHub Comments
@mattfwood I have been having this exact same problem (I am also building a nextjs app with SSR and apollo-client and when it is deployed it doesn’t work). Even with JWT auth. I have looked through your code to try and help my problem, but then I realized something that we both overlooked!
You are right, this part is the problem.
More specifically the problem is
headers
. I, like you, havefunction createClient({ headers }) {***code that returns the apollo client***}
. The headers are then set in the request method when the apollo client is run. If you console.log outheaders
and watch logs on the deployed app, you get back a big long object. One of them beinghost: 'your-front-end-host.com'
. The request setContext overwrites the host of your back-end graphql uri you set earlier. Essentially, you end up replacing your graphql endpoint with your front-end app endpoint. It took me forever to find it because I wasn’t even thinking about ES6 and how it makesheaders,
intoheaders: headers
. Not a problem on localhost because the host would be the same. So, my app works now that I am only pulling out of headers what I need instead of replacing it entirely. For example, this is what I have now:Anyway, I have been banging my head and had the exact same problem as you. Figured I would share a little bit of what I learned in hopes it will help you with your application! Good luck!
After some research, I think I found a solution. It all comes down to how the browser handles 3rd party cookies. Apps cannot set cookies for *.herokuapp.com… Read more here: https://devcenter.heroku.com/articles/cookies-and-herokuapp-com
When I set up my backend on backend.mydomain.com, and the frontend on the same domain (eg. staging.mydomain.com), it works flawlessly, while still using
headers: { cookie: headers && headers.cookie },
as suggested by @dggodfrey: