Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement next-with-apollo with react-apollo SSR? #58

Closed
kumarabhirup opened this issue May 16, 2019 · 16 comments
Closed

How to implement next-with-apollo with react-apollo SSR? #58

kumarabhirup opened this issue May 16, 2019 · 16 comments

Comments

@kumarabhirup
Copy link

I am using react-apollo with Next.js the way it is illustrated in the example from the Next.js repo.

Complete question detail on Spectrum Next.js community.

@lfades
Copy link
Owner

lfades commented May 16, 2019

Hi @kumarabhirup, is this example from the post not working for you ?

import withApollo from 'next-with-apollo'
import ApolloClient from 'apollo-boost'
function createClient({ headers }) {
  return new ApolloClient({
    uri: `${process.env.ENDPOINT}/graphql`,
    request: operation => {
      operation.setContext({
        fetchOptions: {
          credentials: 'include',
        },
        headers
      })
    }
  })
}
export default withApollo(createClient)

@kumarabhirup
Copy link
Author

No that wouldn't, because I also need ssrMode, cache, connectToDevTools for SSR. And If I did that, it gives client.watchQueryis undefined.

Please help 🙏

@lfades
Copy link
Owner

lfades commented May 17, 2019

@kumarabhirup Then add them, it should work, if you get an error please create a reproduction so I can take a look 👍

@kumarabhirup
Copy link
Author

Now I get TypeError: Cannot read property 'displayName' of undefined after the implementation.

Here's the GitHub repo: https://github.com/KumarAbhirup/paprink/tree/noLagOnProdPlease

kumarabhirup added a commit to kumarabhirup/paprink that referenced this issue May 17, 2019
@kumarabhirup
Copy link
Author

Error screenshot 👇
Screenshot 2019-05-17 at 10 13 16 AM

@kumarabhirup
Copy link
Author

I think we need some change in the with-apollo-client.js file

import React from 'react'
import initApollo from './init-apollo'
import Head from 'next/head'
import { getDataFromTree } from 'react-apollo'

export default App => {
  return class Apollo extends React.Component {
    static displayName = 'withApollo(App)'
    static async getInitialProps (ctx) {
      const { Component, router } = ctx

      let appProps = {}
      if (App.getInitialProps) {
        appProps = await App.getInitialProps(ctx)
      }

      // Run all GraphQL queries in the component tree
      // and extract the resulting data
      const apollo = initApollo()
      if (!process.browser) {
        try {
          // Run all GraphQL queries
          await getDataFromTree(
            <App
              {...appProps}
              Component={Component}
              router={router}
              apolloClient={apollo}
            />
          )
        } catch (error) {
          // Prevent Apollo Client GraphQL errors from crashing SSR.
          // Handle them in components via the data.error prop:
          // https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
          console.error('Error while running `getDataFromTree`', error)
        }

        // getDataFromTree does not call componentWillUnmount
        // head side effect therefore need to be cleared manually
        Head.rewind()
      }

      // Extract query data from the Apollo store
      const apolloState = apollo.cache.extract()

      console.log(apollo)

      return {
        ...appProps,
        apolloState
      }
    }

    constructor (props) {
      super(props)
      this.apolloClient = initApollo(props.apolloState)
    }

    render () {
      return <App {...this.props} apolloClient={this.apolloClient} />
    }
  }
}

Error's coming because the code above isn't complying with what withApollo provides.

@kumarabhirup
Copy link
Author

I tried using headers by this: withApollo(({headers}) => (headers)),
but again, didn't work.

console.log(withApollo(({headers}) => (headers))) 👇

function (App) {
        var _a;
        return _a = /** @class */ (function (_super) {
                __extends(WithApollo, _super);
                function WithApollo(props) {
                    var _this = _super.call(this, props) || this;
                    _this.apollo = apollo_1.default(client, {
                        initialState: props.apolloState.data
                    });
                    return _this;
                }
                WithApollo.prototype.render = function () {
                    return react_1.default.createElement(App, __assign({}, this.props, { apollo: this.apollo }));
                };
                return WithApollo;
            }(react_1.default.Component)),
            _a.displayName = "WithApollo(" + getDisplayName(App) + ")",
            _a.propTypes = {
                apolloState: prop_types_1.default.object,
                apollo: prop_types_1.default.object
            },
            _a.getInitialProps = function (appCtx) { return __awaiter(_this, void 0, void 0, function () {
                var Component, router, ctx, headers, apollo, apolloState, getInitialProps, appProps, error_1;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            Component = appCtx.Component, router = appCtx.router, ctx = appCtx.ctx;
                            headers = ctx.req ? ctx.req.headers : {};
                            apollo = apollo_1.default(client, { ctx: ctx, headers: headers });
                            apolloState = {};
                            getInitialProps = App.getInitialProps;
                            appProps = { pageProps: {} };
                            if (!getInitialProps) return [3 /*break*/, 2];
                            ctx.apolloClient = apollo;
                            return [4 /*yield*/, getInitialProps(appCtx)];
                        case 1:
                            appProps = _a.sent();
                            _a.label = 2;
                        case 2:
                            if (ctx.res && (ctx.res.headersSent || ctx.res.finished)) {
                                return [2 /*return*/, {}];
                            }
                            if (!(options.getDataFromTree === 'always' ||
                                (options.getDataFromTree === 'ssr' && ssrMode))) return [3 /*break*/, 7];
                            _a.label = 3;
                        case 3:
                            _a.trys.push([3, 5, , 6]);
                            return [4 /*yield*/, react_apollo_1.getDataFromTree(react_1.default.createElement(App, __assign({}, appProps, { Component: Component, router: router, apolloState: apolloState, apollo: apollo })))];
                        case 4:
                            _a.sent();
                            return [3 /*break*/, 6];
                        case 5:
                            error_1 = _a.sent();
                            // Prevent Apollo Client GraphQL errors from crashing SSR.
                            if (process.env.NODE_ENV !== 'production') {
                                // tslint:disable-next-line no-console This is a necessary debugging log
                                console.error('GraphQL error occurred [getDataFromTree]', error_1);
                            }
                            return [3 /*break*/, 6];
                        case 6:
                            if (ssrMode) {
                                // getDataFromTree does not call componentWillUnmount
                                // head side effect therefore need to be cleared manually
                                head_1.default.rewind();
                            }
                            apolloState.data = apollo.cache.extract();
                            _a.label = 7;
                        case 7: return [2 /*return*/, __assign({}, appProps, { apolloState: apolloState })];
                    }
                });
            }); },
            _a;
    }

@JoonsungUm
Copy link

JoonsungUm commented May 20, 2019

In my case, headers were found in ctx.ctx.req of getInitialProps(ctx). and then pass the headers to initApollo works fine.

@kumarabhirup
Copy link
Author

kumarabhirup commented May 21, 2019

Hey, I tried that and it is retreiving cookies and works fine. BUT... now it is no more Server Side Rendered! It now shows loading everywhere!
This is my frontend repo and my backend repo.

@lfades
Copy link
Owner

lfades commented May 21, 2019

@kumarabhirup Did you fix it by creating your own version of the package ?

btw you're missing await here

@kumarabhirup
Copy link
Author

kumarabhirup commented May 21, 2019

Hey, what do you mean by creating "own version of package"?
Yup, added await there :-)
Bug persists.

Can you please check the code in my backend? May be the way I save cookie be wrong? I tried my best, but I feel everything's matching the next-js example.

@lfades
Copy link
Owner

lfades commented May 21, 2019

@kumarabhirup The backend has nothing to do here, as long as the cookie is set and you can see it devtools then the client should take care of it

@kumarabhirup
Copy link
Author

So, why is it that everytime I use getToken(), SSR stops working? And if it works, it does not get the cookies. I tried console logging complete headers. I found that the cookie needed isn't there! But cookie appears when I perform a mutation. That means, on backend, it secretly signs me in, but frontend does not have any information about it.

When SSR doesn't work, a loading text appears everywhere but then cookies and auth token is rendered. What must be the issue?

On client side, next-with-apollo worked pretty well. On server it didn't. Must be my bad somewhere in the code.

@lfades
Copy link
Owner

lfades commented May 22, 2019

@kumarabhirup I don't really know why but here are some solutions:

  • Make sure that the cookie is set with the proper domain, so the browser is not excluding it, for this case you can for example let the Next.js app set and handle the cookie, instead of your backend, you can see this file to get an idea of how it works.
  • Try not to send the headers and instead just send the cookie you need, use the same file from before to get the cookie from headers and then send the Authorization header, if you can't get the cookie then it's likely that either the browser is not sending it to you, or it's not set, you can always use devTools in the browser to see the cookies.

Always remember that headers are only present in the first render of your site (in SSR), then headers will always be undefined and you should use either document.cookie or the credentials option for fetch operations.

I'm going to close this issue for now because it's not related to this package, I hope you solve it 🙏

@lfades lfades closed this as completed May 22, 2019
kumarabhirup added a commit to kumarabhirup/paprink that referenced this issue Jun 1, 2019
* 📦 NEW: Introduce implementation of apollo SSR
See: https://github.com/zeit/next.js/tree/master/examples/with-apollo
Referencing #35

* 👌 IMPROVE: Remove useless code

* 👌 IMPROVE: Remove useless code

* ❎ SAVE: Add headers code
HAS BUGS

* ❎ SAVE: Use headers

* ❎ SAVE: Show for an issue
lfades/next-with-apollo#58

* ❎ SAVE: Just save what's working

* ❎ SAVE: Copy-Paste code from
https://github.com/zeit/next.js/blob/canary/examples/with-apollo-auth/lib/withApollo.js

* ❎ SAVE: Try more not cry

* ❎ SAVE: Remove request functions

* 📦 NEW: Code checkLoggedIn and redirect script
Imported (copied) from https://github.com/zeit/next.js/tree/canary/examples/with-apollo-auth

* 👌 IMPROVE: Use checkLoggedIn in _app

* ❎ SAVE: Abandon!

* ❎ SAVE: Remove console logs

* 👌 IMPROVE: Add await in _app.js

* 👌 IMPROVE: revert to with-apollo-auth original eg

* 🐛 FIX: fix SSR (#38)

Thanks Harshit Pant

* 👌 IMPROVE: Update lock

* 👌 IMPROVE: gitignore .env.production

* 📦 NEW: Use dep remover

* 🚀 IT WORKS!
@kumarabhirup
Copy link
Author

Now in 2020, when I use hooks, it turns out the problem isn't solved with Next.js 9.
This time, I am not sending a cookie from the backend, I only send a token from the backend.

But it only renders on the Client Side.

I think we should reopen this issue.

@kumarabhirup
Copy link
Author

I am using @apollo/react-hooks. Just wondering, if next-with-apollo supports it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants