GraphQL 解析器上下文在 Playground 中工作,但在官方 NextJS 入门示例中不在客户端中

GraphQL resolver context working in Playground but not client in official NextJS starter example

我正在重新配置我的 NextJS/Apollo 应用程序以允许使用 GraphQL API 路由的 SSG,并且我正在使用 this official NextJS starter example 作为客户端配置的基础。

虽然在我自己的应用程序中,但我 运行 遇到了一个有趣的问题,所以我回到了入门示例并尝试重现它,并且能够重现。问题是,如果没有任何上下文对象传递到查询解析器,一切都可以正常工作(在操场上和客户端上)。但是,当您引入上下文对象并将其传递给解析器时,它在 playground 中运行良好,但从客户端触发时上下文对象为 undefined。这是来自官方 NextJS 入门示例的代码,我会在添加任何内容的地方发表评论。

graphql.js

import { ApolloServer } from "apollo-server-micro";
import { schema } from "../../apollo/schema";

const apolloServer = new ApolloServer({
    schema,
    context: {        // 
        foo: "bar",   // this is the context object I've added
    },                //
});

export const config = {
    api: {
        bodyParser: false,
    },
};

export default apolloServer.createHandler({ path: "/api/graphql" });

typedefs.js

import { gql } from '@apollo/client'

export const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    status: String!
  }

  type Query {
    viewer: User
  }
`

schema.js

import { makeExecutableSchema } from 'graphql-tools'
import { typeDefs } from './type-defs'
import { resolvers } from './resolvers'

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

resolvers.js

export const resolvers = {
    Query: {
        viewer: (_parent, _args, context, _info) => {
            console.log("context", context); // console log check that I've added
            return { id: 1, name: "John Smith", status: "cached" };
        },
    },
};

当我在 GraphQL 游乐场 运行 查询 API 时,它给了我正确的响应,并且在我的终端控制台中 returns 上下文 foo: bar 来自控制台日志的对象,因此在服务器中正确传递了上下文对象。但是,当我在浏览器中访问索引页面时,是这样的:

index.js

import gql from "graphql-tag";
import Link from "next/link";
import { useQuery } from "@apollo/client";
import { initializeApollo } from "../apollo/client";

const ViewerQuery = gql`
    query ViewerQuery {
        viewer {
            id
            name
            status
        }
    }
`;

const Index = () => {
    const {
        data: { viewer },
    } = useQuery(ViewerQuery);

    return (
        <div>
            You're signed in as {viewer.name} and you're {viewer.status} goto{" "}
            <Link href="/about">
                <a>static</a>
            </Link>{" "}
            page.
        </div>
    );
};

export async function getStaticProps() {
    const apolloClient = initializeApollo();

    await apolloClient.query({
        query: ViewerQuery,
    });

    return {
        props: {
            initialApolloState: apolloClient.cache.extract(),
        },
    };
}

export default Index;

...查看器名称和查看器状态已呈现,因此查询实际上正在发生,但在控制台中,context 对象控制台日志正在返回 undefined。因此,当在客户端中使用时,context 会以某种方式丢失。我觉得这很有趣,因为这是一个正式的 NextJS 入门示例,除非他们将客户端设置为不接受解析器中的上下文,否则我看不出问题是什么。如果 客户端未设置为接受上下文的情况,是否有任何其他客户端设置接受上下文的官方示例?

现在这是一个很长的问题,但这里是 client.js 设置:

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

let apolloClient;

function createIsomorphLink() {
    if (typeof window === "undefined") {
        const { SchemaLink } = require("@apollo/client/link/schema");
        const { schema } = require("./schema");
        return new SchemaLink({ schema });
    } else {
        const { HttpLink } = require("@apollo/client/link/http");
        return new HttpLink({
            uri: "http://localhost:3000/api/graphql",
            credentials: "same-origin",
        });
    }
}

function createApolloClient() {
    return new ApolloClient({
        ssrMode: typeof window === "undefined",
        link: createIsomorphLink(),
        cache: new InMemoryCache(),
    });
}

export function initializeApollo(initialState = null) {
    const _apolloClient = apolloClient ?? createApolloClient();

    // If your page has Next.js data fetching methods that use Apollo Client, the initial state
    // gets hydrated here
    if (initialState) {
        _apolloClient.cache.restore(initialState);
    }
    // For SSG and SSR always create a new Apollo Client
    if (typeof window === "undefined") return _apolloClient;
    // Create the Apollo Client once in the client
    if (!apolloClient) apolloClient = _apolloClient;

    return _apolloClient;
}

export function useApollo(initialState) {
    const store = useMemo(() => initializeApollo(initialState), [initialState]);
    return store;
}

我恳请任何人克隆这个官方存储库,看看他们是否能弄清楚如何让上下文在客户端中工作,或者是否有人知道为什么这个客户端设置不适用于上下文并且知道客户端设置可以接受解析器上下文,我将不胜感激。这个问题已经困扰我两天了!

我找到问题了。客户端配置使用 SchemaLink 作为 http 请求,上下文是在 SchemaLink 构造函数中传递的,而不是在服务器选项中传递的,因为上下文是在 http headers 和 httpLink 中传递的.