useQuery返回未定义,但是在gql操场上返回数据

时间:2020-01-26 17:21:05

标签: node.js reactjs graphql react-apollo apollo-server

<connectionStrings>
  <add name="ConnectionName"
    connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|DatabaseName.mdf;Integrated Security=True;User Instance=True"
    providerName="System.Data.SqlClient" />
</connectionStrings>

Apollo客户端在react应用上返回未定义,但在gql操场上返回数据,我不明白为什么它不能在客户端上工作而在graphql操场上工作。

架构

我为用户查询定义了并集,以进行错误处理。

"@apollo/react-hooks": "^3.1.3",
"apollo-client": "^2.6.8",

查询解析器

type Query {
  user(id: ID!): UserReturn!
}

union UserReturn = User | Error

type User {
  id: ID!
  username: String!
  email: String
  profileUrl: String
  createdAt: Date
  ads: [Doc!]!
}


type Error {
  message: String
  code: ID
}

在gql游乐场查询时

在graphql操场上,查询有效。

Screenshot from 2020-01-25 23-44-01_censored

在客户端

 async user(_, { id }, { User }) {
    console.log('query - User')
    try {
      await delay(1000 * 3)
      const user = await User.findById(id).populate('userData')
      console.log(user)
      if (!user) return {
        __typename: 'Error',
        message: 'User not found.',
        code: id
      }

      const { _id: id, username, email, createdAt, userData: { profileUrl } } = user

      console.log(username)
      return {
        __typename: 'User',
        id,
        username,
        email,
        createdAt,
        profileUrl
      }
    } catch (err) {
      console.log(err)
      return {
        __typename: 'Error',
        message: 'Something went wrong while getting user.',
        code: LogBack(err, `query/user?id=${id}`, __filename)
      }
    }
  }

useQuery运行,但返回undefiend。

4 个答案:

答案 0 :(得分:0)

可能有用的方法,您知道在哪里调用{data} 您还可以查找错误和console.log('Error:',error)

检查apollo客户端查询文档

类似这样的东西,并查看错误消息,应该有帮助!

import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag';

const GET_GREETING = gql`
  query getGreeting($language: String!) {
    greeting(language: $language) {
      message
    }
  }
`;

function Hello() {
    const { loading, error, data } = useQuery(GET_GREETING, {
    variables: { language: 'english' },
  });
  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;
  return <h1>Hello {data.greeting.message}!</h1>;
}

答案 1 :(得分:0)

这是一个较晚的答案,但是我遇到同样的问题,我的playGround返回了正确的响应,但没有useQuery挂钩。

我的问题是,给您查询的变量(在您的情况下为'id')的类型为String而不是Number。

答案 2 :(得分:0)

请耐心等待,这个答案很长

我也遇到了这个问题。使用片段(在这种情况下为内联)和接口时,似乎出现了问题。我设法通过将正确的自省数据传递给Apollo的启发式片段匹配器来解决它(请参阅第3步)。

以下是有关解决方法的详细分步指南:

1-验证控制台警告。

验证控制台中是否存在警告(以下是我身上发生的一个示例)。这些是与默认启发式片段匹配器冲突的字段:

Firefox Dev Console with warnings

通过阅读Apollo文档,我发现了以下内容:

默认情况下,Apollo Client的缓存将使用启发式片段 匹配器,如果包含结果,则假定片段匹配 其选择集中的所有字段,并且在任何字段都不匹配时 失踪。这在大多数情况下都有效,但这也意味着阿波罗 客户端无法为您检查服务器响应,也无法告诉您 当您使用手动将无效数据写入存储时 update,updateQuery,writeQuery等。启发式片段 结合使用片段或联合时,匹配器将无法正常工作 界面。 Apollo Client将通过控制台通知您 警告(正在开发中),如果尝试使用默认启发式 具有并集/接口的片段匹配器。的 IntrospectionFragmentMatcher是与之合作的解决方案 并集/接口,下面将对其进行详细说明。

第2版的更多信息在这里: https://www.apollographql.com/docs/react/v2.6/data/fragments/#fragments-on-unions-and-interfaces

v3的更多信息在这里: https://www.apollographql.com/docs/react/data/fragments/#using-fragments-with-unions-and-interfaces

要解决此问题,我们需要将IntrospectionResultData传递给Apollo客户端(请参阅第3步)。但是在此之前,我们需要生成文件或数据。

您有3个选择。手动或自动(远程或本地)。

2-生成内省文件

选择以下一个选项(所有这些最终都一样)。选择所有内容之前,请先阅读所有内容。

2.1-选项A-手动生成文件。

使用以下模式将其调整为适合您自己的模式。 注意,以下是TypeScript代码。如果您使用的是普通JS,请删除type

请注意,在我的情况下,我在.gql文件中具有以下形式的联合类型:

   # GraphQL code omitted. 
  union PlanningResult = Planning | PlanningTechnical
// For Apollo V 2.x
export interface IntrospectionResultData {
  __schema: {
    types: {
      kind: string;
      name: string;
      possibleTypes: {
        name: string;
      }[];
    }[];
  };
}

const result: IntrospectionResultData = {
  __schema: {
    types: [
      {
        kind: 'UNION',
        name: 'PlanningResult',
        possibleTypes: [
          {
            name: 'Planning',
          },
          {
            name: 'PlanningTechnical',
          },
        ],
      },
    ],
  },
};
export default result;

// For Apollo V3:

      export interface PossibleTypesResultData {
        possibleTypes: {
          [key: string]: string[]
        }
      }
      const result: PossibleTypesResultData = {
  "possibleTypes": {
    "PlanningResult": [
      "Planning",
      "PlanningTechnical"
    ]
  }
};
      export default result;
    

完成此操作后,请继续执行步骤3。

2.2-选项B-自动远程进近。

这是如果您的模式位于远程服务器中,并且您想获取它。这是直接从Apollo文档中提取的脚本。对于自动方法,您可以按照Apollo文档中所述直接获取架构:

// This is for V2 only, for V3 use the link down below (They're not the same!).

// For V2: https://www.apollographql.com/docs/react/v2.6/data/fragments/#fragments-on-unions-and-interfaces
// For V3 please, go to https://www.apollographql.com/docs/react/data/fragments/#generating-possibletypes-automatically

const fetch = require('node-fetch');
const fs = require('fs');

fetch(`${YOUR_API_HOST}/graphql`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    variables: {},
    query: `
      {
        __schema {
          types {
            kind
            name
            possibleTypes {
              name
            }
          }
        }
      }
    `,
  }),
})
  .then(result => result.json())
  .then(result => {
    // here we're filtering out any type information unrelated to unions or interfaces
    const filteredData = result.data.__schema.types.filter(
      type => type.possibleTypes !== null,
    );
    result.data.__schema.types = filteredData;
    fs.writeFile('./fragmentTypes.json', JSON.stringify(result.data), err => {
      if (err) {
        console.error('Error writing fragmentTypes file', err);
      } else {
        console.log('Fragment types successfully extracted!');
      }
    });
  });

这将生成具有__schema和适当类型的json文件。完成此操作后,请继续执行步骤3。

2.3-选项C-自动局部进近

上面的选项对我来说很困难,因为我的模式位于身份验证墙后面。幸运的是,我确实可以直接本地访问.gql文件,并且能够生成自省文件。继续阅读:

我们使用graphql-code-generator为我们生成内省文件。

转到您的后端代码,或您的graphql.gql文件所在的位置,然后执行:

  1. 安装GraphQL Code Generator
yarn add graphql

yarn add -D @graphql-codegen/cli
  1. 运行初始化向导:
yarn graphql-codegen init
  1. 填写详细信息(适合您自己) 就我而言,我选择了:
  • 后端-API或服务器,使用React构建的应用程序
  • 您的架构在哪里? ./appsync/appSync.gql
  • 选择插件:Fragment Matcher(也可以选择其他插件,这是重要的一个!)
  • 文档:./appsync/generated/introspection.ts(要在此处输出文件)

这将生成一个codegen.yml,其中将包含插件和运行graphql-code-generator的配置。

这是我的:

overwrite: true
schema: "./appsync/appSync.gql"
# documents: "./appsync/**/*.gql"
generates:
  ./appsync/generated/introspection.ts:
    plugins:
      # - "typescript"
      # - "typescript-operations"
      # - "typescript-resolvers"
      # - "typescript-react-apollo"
      - "fragment-matcher"
    config:
    # NOTE: Remember to specify the CORRECT Apollo Client Version
      apolloClientVersion: 2.6
  ./graphql.schema.json:
    plugins:
      - "introspection"

我已经评论了对我们的任务不重要的部分。

然后(非常重要!)运行:

yarn install

因为该向导会将软件包添加到我们的package.json

然后,生成代码:

yarn generate

这将输出introspection.ts文件,该文件需要包含在Apollo中才能继续。

3-将自省文件注入ApolloClient

现在,在您的前端代码中,将introspection.ts文件复制到您的仓库中(如果尚未存在),并包含它:

注意:我已将文件重命名为fragmentTypes.ts,并将其包含在apollo文件夹中:

For V2:
import ApolloClient from 'apollo-client/ApolloClient';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import { InMemoryCache } from 'apollo-cache-inmemory/lib/inMemoryCache';
// The file we just generated. If it's a .json file 
// remember to include the .json extension
import introspectionQueryResultData from './apollo/fragmentTypes';

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData,
});

export const globalClient = new ApolloClient({
  link,
  cache: new InMemoryCache({ fragmentMatcher }),
});

For V3:
import { InMemoryCache, ApolloClient } from '@apollo/client';
// In case you used graphql-code-generator
// import introspectionQueryResultData from './apollo/fragmentTypes';
// The file we just generated. If it's a .json file 
// remember to include the .json extension
import possibleTypes from './path/to/possibleTypes.json';

const cache = new InMemoryCache({
  possibleTypes,
});
const client = new ApolloClient({
  // ...other arguments...
  cache,
});

此后,您的控制台警告应消失,查询和变异应正常执行。

答案 3 :(得分:0)

这可能与您的情况无关,但我的是命名问题。

  const { isLoading, error, blogs } = useQuery(BLOGS);

从钩子返回的对象是data而不是blogs