我有一个Next.js应用程序,我正在使用apollo-server-micro和apollo-client。当用户遇到登录变异时,我无法设置cookie会话。这是我的server.js设置(npm start)
const express = require('express');
const next = require('next');
const { apolloServer } = require('pages/api/graphql');
const cookieSession = require('cookie-session');
const dev = process.env.NODE_ENV !== 'production';
const app = nextApp({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use(express.static('./public'));
server.use(
'/api',
cookieSession({
httpOnly: false,
keys: ['super-secret-key'],
maxAge: 24 * 60 * 60 * 1000,
name: 'xyz',
})
);
server.get('*', handle);
server.listen(8000, () => {
console.log('Server running on port 8000');
})
})在pages/api/graphql.js中,我有以下代码(直接来自Next.js文档)
import { ApolloServer } from 'apollo-server-micro'
import { schema } from '../../apollo/schema'
export const apolloServer = new ApolloServer({
schema,
context: (ctx) => {
return ctx.req;
}
})
export const config = {
api: {
bodyParser: false
}
}
export default apolloServer.createHandler({ path: '/api/graphql' })下面是用于登录的变体
const jobs = require('../data/jobs');
const users = require('../data/users');
const getUserByEmail = email => users.find(user => user.email === email);
export const resolvers = {
Query: {
viewer (_parent, _args, _context, _info) {
return { id: 1, name: 'John Smith', status: 'cached' }
},
jobs: (parent, args, context, info) => {
return jobs;
}
},
Mutation: {
login: (parent, args, context, info) => {
const { email } = args;
const user = getUserByEmail(email);
console.log(context.headers);
if (user) {
context.req = {
...context.req,
session: user
}
return user;
}
}
}
}现在还没有加密,我只想让它启动并运行起来。如何设置会话(以便cookie显示在Chrome中,并可用于将来的传入请求)
更新: Apollo客户端配置
import React from 'react'
import Head from 'next/head'
import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
let apolloClient = null
/**
* Creates and provides the apolloContext
* to a next.js PageTree. Use it by wrapping
* your PageComponent via HOC pattern.
* @param {Function|Class} PageComponent
* @param {Object} [config]
* @param {Boolean} [config.ssr=true]
*/
export function withApollo (PageComponent, { ssr = true } = {}) {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
const client = apolloClient || initApolloClient(apolloState)
return (
<ApolloProvider client={client}>
<PageComponent {...pageProps} />
</ApolloProvider>
)
}
// Set the correct displayName in development
if (process.env.NODE_ENV !== 'production') {
const displayName =
PageComponent.displayName || PageComponent.name || 'Component'
if (displayName === 'App') {
console.warn('This withApollo HOC only works with PageComponents.')
}
WithApollo.displayName = `withApollo(${displayName})`
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async ctx => {
const { AppTree } = ctx
// Initialize ApolloClient, add it to the ctx object so
// we can use it in `PageComponent.getInitialProp`.
const apolloClient = (ctx.apolloClient = initApolloClient())
// Run wrapped getInitialProps methods
let pageProps = {}
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx)
}
// Only on the server:
if (typeof window === 'undefined') {
// When redirecting, the response is finished.
// No point in continuing to render
if (ctx.res && ctx.res.finished) {
return pageProps
}
// Only if ssr is enabled
if (ssr) {
try {
// Run all GraphQL queries
const { getDataFromTree } = await import('@apollo/react-ssr')
await getDataFromTree(
<AppTree
pageProps={{
...pageProps,
apolloClient
}}
/>
)
} 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 = apolloClient.cache.extract()
return {
...pageProps,
apolloState
}
}
}
return WithApollo
}
/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {Object} initialState
*/
function initApolloClient (initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return createApolloClient(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = createApolloClient(initialState)
}
return apolloClient
}
/**
* Creates and configures the ApolloClient
* @param {Object} [initialState={}]
*/
function createApolloClient (initialState = {}) {
const ssrMode = typeof window === 'undefined'
const cache = new InMemoryCache().restore(initialState)
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
ssrMode,
link: createIsomorphLink(),
cache
})
}
function createIsomorphLink () {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema')
const { schema } = require('./schema')
return new SchemaLink({ schema })
} else {
const { HttpLink } = require('apollo-link-http')
return new HttpLink({
uri: '/apii/graphql',
credentials: 'same-origin'
})
}
}更新:使用apollo micro的Apollo-server配置
import { ApolloServer } from 'apollo-server-micro'
import { schema } from '../../apollo/schema'
export const apolloServer = new ApolloServer({
schema,
context: (ctx) => {
return ctx.req;
}
})
export const config = {
api: {
bodyParser: false
}
}
export default apolloServer.createHandler({ path: '/api/graphql' })发布于 2019-10-24 11:08:19
如果您使用的是SchemaLink,我认为您可以直接从PageContext获取req对象,并使用它来重新构造GraphQL上下文对象。
const { AppTree, req } = ctx
// elsewhere
new SchemaLink({ schema, context: { req } })这里有一个不使用SchemaLink的示例,您可以在项目中采用它。关键是从PageContext中提取Cookie值。然后,您可以使用它来创建一个填充Cookie头的自定义fetch实现,然后将这个fetch函数传递给您的HttpLink构造函数。
let apolloClient: ApolloClient<any>
export const getClient = (
initialState: NormalizedCacheObject = {},
cookie?: string,
): ApolloClient<any> => {
const fetch: WindowOrWorkerGlobalScope['fetch'] = async (url, init = {}) => {
const headers = { ...init.headers } as Record<string, string>
if (cookie) {
headers.Cookie = cookie
}
const response = await unfetch(url, {
...init,
headers,
})
return response
}
return new ApolloClient<any>({
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: 'http://localhost:3000/graphql',
credentials: 'same-origin',
fetch,
}),
cache: new InMemoryCache().restore(initialState),
})
}
export const initApollo = (
initialState?: NormalizedCacheObject,
cookie?: string,
): ApolloClient<any> => {
if (typeof window === 'undefined') {
return getClient(initialState, cookie)
}
if (!apolloClient) {
apolloClient = getClient(initialState, cookie)
}
return apolloClient
}
export const withApollo = (PageComponent: NextPage, { ssr = true } = {}) => {
const WithApollo = ({ apolloState, ...pageProps }: WithApolloProps) => {
const client = apolloClient || initApollo(apolloState)
return (
<ApolloProvider client={client} >
<PageComponent {...pageProps} />
</ApolloProvider>
)
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async (ctx: PageContext) => {
const { AppTree } = ctx
const cookie = (ctx.req && ctx.req.headers.cookie) || undefined
const apolloClient = (ctx.apolloClient = initApollo({}, cookie))
let pageProps = {}
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx)
}
if (typeof window === 'undefined') {
if (ctx.res && ctx.res.finished) {
return pageProps
}
if (ssr) {
try {
const { getDataFromTree } = await import('@apollo/react-ssr')
await getDataFromTree(
<AppTree
pageProps={{
...pageProps,
apolloClient,
}}
/>,
)
} catch (error) {
console.error('Error while running `getDataFromTree`', error)
}
Head.rewind()
}
}
const apolloState = apolloClient.cache.extract()
return {
...pageProps,
apolloState,
}
}
}
return WithApollo
}另外,您需要确保在初始化Apollo Server时正确设置了上下文。不要只返回req对象,因为这会将您的上下文设置为req对象。取而代之的是:
context: ({ req }) => {
return { req };
}然后确保您在解析器中正确地改变了会话:
context.req.session.user = user实际上,您的代码覆盖了用户的session对象,这不是您想要做的。在会话上设置user属性意味着您可以在代码中的其他位置使用context.req.session.user访问它。
发布于 2021-06-28 14:59:33
下面是本文https://www.rockyourcode.com/nextjs-with-apollo-ssr-cookies-and-typescript/中的一个解决方案
import {
ApolloClient,
InMemoryCache,
NormalizedCacheObject,
} from "@apollo/client";
import { createUploadLink } from "apollo-upload-client";
import merge from "deepmerge";
import { IncomingHttpHeaders } from "http";
import fetch from "isomorphic-unfetch";
import isEqual from "lodash/isEqual";
import { AppProps } from "next/app";
import { useMemo } from "react";
const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined;
const createApolloClient = (headers: IncomingHttpHeaders | null = null) => {
const enhancedFetch = (url: RequestInfo, init: RequestInit) => {
return fetch(url, {
...init,
headers: {
...init.headers,
"Access-Control-Allow-Origin": "*", // TODO: Check this out
Cookie: headers?.cookie ?? "",
},
}).then((response) => response);
};
return new ApolloClient({
ssrMode: typeof window === "undefined",
link: createUploadLink({
uri: "http://localhost:4000/graphql",
// Make sure that CORS and cookies work
fetchOptions: {
mode: "cors",
},
credentials: "include",
fetch: enhancedFetch,
}),
cache: new InMemoryCache(),
});
};
type InitialState = NormalizedCacheObject | undefined;
interface IInitializeApollo {
headers?: IncomingHttpHeaders | null;
initialState?: InitialState | null;
}
export const initializeApollo = (
{ headers, initialState }: IInitializeApollo = {
headers: null,
initialState: null,
},
) => {
const _apolloClient = apolloClient ?? createApolloClient(headers);
if (initialState) {
const existingCache = _apolloClient.extract();
const data = merge(initialState, existingCache, {
arrayMerge: (destinationArray, sourceArray) => [
...sourceArray,
...destinationArray.filter((d) =>
sourceArray.every((s) => !isEqual(d, s)),
),
],
});
_apolloClient.cache.restore(data);
}
if (typeof window === "undefined") return _apolloClient;
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
};
export const addApolloState = (
client: ApolloClient<NormalizedCacheObject>,
pageProps: AppProps["pageProps"],
) => {
if (pageProps?.props) {
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
}
return pageProps;
};
export function useApollo(pageProps: AppProps["pageProps"]) {
const state = pageProps[APOLLO_STATE_PROP_NAME];
const store = useMemo(
() => initializeApollo({ initialState: state }),
[state],
);
return store;
}https://stackoverflow.com/questions/58518568
复制相似问题