我将 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 不起作用。
我已经调查了这个问题,这似乎是一个常见的问题,即 cookie 没有随请求一起发送:
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 访问该站点时,它将不再自动登录(它使用我的初始方法自动登录但不会执行 SSR制作)
人们已经提到,这可能与 Now 或 Heroku 在部署应用程序后获得的生成的 URL 中使用子域有关,并使用自定义域来解决问题。我尝试使用自定义域,但问题仍然存在。我的域设置是这样的:
前端域名:mysite.com 后台域名:api.mysite.com
这里有没有人遇到过这个问题并能够解决它?
如果您发现我的配置有问题或我如何设置我的域,请告诉我。
您应该如下添加 cookies 选项。请确保您的浏览器中有 cookie,即 csrftoken。它应该可用。希望它有效。
const request = 异步操作 => { const contextObj = { 获取选项:{ 凭据:'包括' }, //################ 在这里更改########## 标题:{ ...标题 } 饼干: { ...饼干 } //###################################### }; operation.setContext(contextObj); };
官方有问题或者缺少NextJs apollo example 这是在这个issue
中报道的我只是把让我解决这个问题的评论拼凑起来
这里是对官方示例的修改,由 derozan10 发布,源自 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),
});
}
为了让它工作,我还在后端更改了我的 CORS 选项
// 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));
...
我还更新了依赖项,直到我可以达到“无纱线警告”状态
"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"
}