admin管理员组

文章数量:1314292

I am using Next JS with Apollo and have set it up using the following config in my with-data HOC:

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { withClientState } from 'apollo-link-state';
import { getMainDefinition } from 'apollo-utilities';
import { ApolloLink, Observable, split  } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import withApollo from 'next-with-apollo';
import { SubscriptionClient } from 'subscriptions-transport-ws';

import { endpoint, prodEndpoint, WSendpoint, WSprodEndpoint } from '../config';

import defaults from '../apollo-state/graphql/default-state';
import Mutation from '../apollo-state/resolvers/mutation-resolvers';

const wsClient = process.browser ? new SubscriptionClient(process.env.NODE_ENV === 'development' ? WSendpoint : WSprodEndpoint, {
  reconnect: true,
}) : null;


function createClient({ headers }) {
  const wsLink = process.browser ? new WebSocketLink(wsClient) : null;
  const httpLink = new HttpLink({
    uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
    credentials: 'include',
  })

  const link = process.browser ? split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  ) : httpLink;

  const cache = new InMemoryCache();

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

  const requestLink = new ApolloLink((operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(oper => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            plete: observerplete.bind(observer),
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
  );
  // end custom config functions
  const apolloClient = new ApolloClient({
      credentials: 'include',
      ssrMode: !process.browser,
      link: ApolloLink.from([
        onError(({ graphQLErrors, networkError }) => {
          if (graphQLErrors) {
            console.log(graphQLErrors)
          }
          if (networkError) {
            console.log(networkError)
          }
        }),
        requestLink,
        withClientState({
          defaults, // default state
          resolvers: {
            Mutation // mutations
          },
          cache
        }),
        link
      ]),
      cache
  }); // end new apollo client
  return apolloClient;
}

export { wsClient };
export default withApollo(createClient);

Locally everything works fine. I can log in, it automatically logs me in when I visit the site and SSR works no problem. However when I deploy to either Next or Heroku the SSR does not work.

I've looked into the issue and it seems as though it's a mon problem with cookies not being sent with requests:

The issue appears to lye with this part of the Apollo config where the headers are sometimes not defined therefore the cookie is not being sent:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

Some of the workarounds people have mentioned are to manually set the cookie header if headers exists:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers: {
        cookie: headers && headers.cookie
      }
    };
    operation.setContext(contextObj);
  }; 

The above amend to the code fixes the server side rendering however when I visit the site with a logged in cookie in my browser it will then no longer log me in automatically (it logs me in automatically with my initial method but wont do SSR on production)

People have mentioned it can be to do with Now or Heroku using a subdomain in the generated URLs you get once you deploy the app and to use a custom domain to resolve the issue. I tried using a custom domain however I am still experiencing the issue. My domain setup is like so:

Frontend domain: mysite backend domain: api.mysite

Has anyone here experienced the issue and been able to resolve it?

Please let me know if you spot something wrong with my config or how I have setup my domains.

I am using Next JS with Apollo and have set it up using the following config in my with-data HOC:

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { withClientState } from 'apollo-link-state';
import { getMainDefinition } from 'apollo-utilities';
import { ApolloLink, Observable, split  } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import withApollo from 'next-with-apollo';
import { SubscriptionClient } from 'subscriptions-transport-ws';

import { endpoint, prodEndpoint, WSendpoint, WSprodEndpoint } from '../config';

import defaults from '../apollo-state/graphql/default-state';
import Mutation from '../apollo-state/resolvers/mutation-resolvers';

const wsClient = process.browser ? new SubscriptionClient(process.env.NODE_ENV === 'development' ? WSendpoint : WSprodEndpoint, {
  reconnect: true,
}) : null;


function createClient({ headers }) {
  const wsLink = process.browser ? new WebSocketLink(wsClient) : null;
  const httpLink = new HttpLink({
    uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
    credentials: 'include',
  })

  const link = process.browser ? split(
    // split based on operation type
    ({ query }) => {
      const { kind, operation } = getMainDefinition(query);
      return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
  ) : httpLink;

  const cache = new InMemoryCache();

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

  const requestLink = new ApolloLink((operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(oper => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            plete: observer.plete.bind(observer),
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
  );
  // end custom config functions
  const apolloClient = new ApolloClient({
      credentials: 'include',
      ssrMode: !process.browser,
      link: ApolloLink.from([
        onError(({ graphQLErrors, networkError }) => {
          if (graphQLErrors) {
            console.log(graphQLErrors)
          }
          if (networkError) {
            console.log(networkError)
          }
        }),
        requestLink,
        withClientState({
          defaults, // default state
          resolvers: {
            Mutation // mutations
          },
          cache
        }),
        link
      ]),
      cache
  }); // end new apollo client
  return apolloClient;
}

export { wsClient };
export default withApollo(createClient);

Locally everything works fine. I can log in, it automatically logs me in when I visit the site and SSR works no problem. However when I deploy to either Next or Heroku the SSR does not work.

I've looked into the issue and it seems as though it's a mon problem with cookies not being sent with requests:

https://github./apollographql/apollo-client/issues/4455

https://github./apollographql/apollo-client/issues/4190

https://github./apollographql/apollo-client/issues/4193

The issue appears to lye with this part of the Apollo config where the headers are sometimes not defined therefore the cookie is not being sent:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers
    };
    operation.setContext(contextObj);
  }; 

Some of the workarounds people have mentioned are to manually set the cookie header if headers exists:

  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      headers: {
        cookie: headers && headers.cookie
      }
    };
    operation.setContext(contextObj);
  }; 

The above amend to the code fixes the server side rendering however when I visit the site with a logged in cookie in my browser it will then no longer log me in automatically (it logs me in automatically with my initial method but wont do SSR on production)

People have mentioned it can be to do with Now or Heroku using a subdomain in the generated URLs you get once you deploy the app and to use a custom domain to resolve the issue. I tried using a custom domain however I am still experiencing the issue. My domain setup is like so:

Frontend domain: mysite. backend domain: api.mysite.

Has anyone here experienced the issue and been able to resolve it?

Please let me know if you spot something wrong with my config or how I have setup my domains.

Share Improve this question edited May 27, 2019 at 9:45 red house 87 asked May 22, 2019 at 19:22 red house 87red house 87 2,41512 gold badges61 silver badges110 bronze badges 2
  • If you had any other solution, please share with us, it is really annoing. If you don't have any other, the solution pointed by @Ngatia Frankline really works to me. – Herbert Pimentel Commented Dec 19, 2019 at 21:30
  • I ended up setting the cookie on the Next JS server. Here is the repository I copied for this approach: github./Alexloof/Next-GraphQL-Blog – red house 87 Commented Dec 20, 2019 at 13:54
Add a ment  | 

3 Answers 3

Reset to default 2

Late but try this

you should add cookies option as below. Please make sure you have a cookiein your browser i.e. csrftoken. It should be available. Hope it works.


  const request = async operation => {
    const contextObj = {
      fetchOptions: {
        credentials: 'include'
      },
      //################ CHANGE HERE ##########
      headers: {
        ...header
      }
      cookies: {
        ...cookies
      }
      //######################################

    };
    operation.setContext(contextObj);
  };

There is a problem or lack with the official NextJs apollo example Which is reported in this issue

I'll just stitch together the ments that allowed me to solve this issue

Here is a modification to the official example, posted by derozan10, derived from a post for an anterior version of the example by mzygmunt

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { endpoint } from '../config';

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.

  const enchancedFetch = (url, init) =>
    fetch(url, {
      ...init,
      headers: {
        ...init.headers,
        Cookie: ctx.req.headers.cookie,
      },
    }).then(response => response);

  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link: new HttpLink({
      uri: endpoint, // Server URL (must be absolute)
      credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
      fetch: ctx ? enchancedFetch : fetch,
    }),
    cache: new InMemoryCache().restore(initialState),
  });
}

To make it work I also changed my CORS options on the backend


// a graphql-yoga + prisma 1 backend (Wes Boss' Advanced React class)
...
const cors = require("cors");

const server = createServer();

var corsOptions = {
  origin: process.env.FRONTEND_URL,
  credentials: true
};
server.express.use(cors(corsOptions));
...

And I also updated the dependencies until I could reach a 'no yarn warning' state

"dependencies": {
    "@apollo/react-hooks": "^3.1.5",
    "@apollo/react-ssr": "^3.1.5",
    "@babel/core": "^7.1.2",
    "@types/react": "^16.8.0",
    "apollo-cache-inmemory": "^1.6.6",
    "apollo-client": "^2.6.9",
    "apollo-link-http": "^1.5.17",
    "apollo-utilities": "^1.3.2",
    "graphql": "14.3.1",
    "graphql-tag": "^2.10.3",
    "isomorphic-unfetch": "^3.0.0",
    "next": "latest",
    "prop-types": "^15.6.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  },
  "devDependencies": {
    "babel-plugin-graphql-tag": "^2.5.0"
  }

If your problem is with an SSR request, here is an example of how it worked for me:

Apollo client config:

import {ApolloClient, HttpLink, InMemoryCache} from "@apollo/client";

const httpLink = new HttpLink({
  uri: `${process.env.NEXT_PUBLIC_URL_SITE}/api/graphql`,
  credentials: "same-origin",
});

export const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

SSR request:


export const getServerSideProps: GetServerSideProps = async ({req}) => {
    const {data} = await client.query({
      query: QUERY,
      context: {
        headers: {
          cookie: req?.headers?.cookie ?? null,
        },
      },
    });
  
    return {
      props: {data},
    };
  };
  

You have to add manually

context: {
        headers: {
          cookie: req?.headers?.cookie ?? null,
        },
}

I'm using:

  • "@apollo/client": "^3.7.1",
  • "@apollo/server": "^4.1.1",
  • "graphql": "^16.6.0",
  • "next": "13.1.1",
  • "react": "18.2.0",

本文标签: javascriptNext js and ApolloCookie not being passedStack Overflow