Next js и Apollo - Cookie не передается - PullRequest
6 голосов
/ 22 мая 2019

Я использую Next JS с Apollo и настроил его, используя следующую конфигурацию в моем 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),
            complete: observer.complete.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);

Локально все отлично работает. Я могу войти, он автоматически регистрирует меня, когда я захожу на сайт, и SSR работает без проблем. Однако при развертывании в Next или Heroku SSR не работает.

Я изучил проблему, и кажется, что это обычная проблема, когда куки не отправляются с запросами:

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

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

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

Похоже, что проблема связана с этой частью конфигурации Apollo, где заголовки иногда не определены, поэтому cookie не отправляется:

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

Некоторые из упомянутых обходных путей - это ручная установка заголовка cookie, если заголовки существуют:

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

Вышеуказанная поправка к коду исправляет рендеринг на стороне сервера, однако, когда я захожу на сайт с зарегистрированным файлом cookie в моем браузере, он больше не будет входить в систему автоматически (он входит в систему автоматически с моим первоначальным методом, но не будет ССР на производстве)

Люди уже упоминали, что это может быть связано с Now или Heroku, использующими субдомен в сгенерированных URL-адресах, которые вы получаете после развертывания приложения, и использования настраиваемого домена для решения проблемы. Я пытался использовать пользовательский домен, но проблема все еще не устранена. Моя настройка домена выглядит так:

Фронтенд домен: mysite.com внутренний домен: api.mysite.com

Кто-нибудь здесь испытывал проблему и был в состоянии решить ее?

Пожалуйста, дайте мне знать, если вы заметили что-то не так с моей конфигурацией или как я настроил свои домены.

...