Apollo-link: 将 node-fetch 与 apollo-link-http 结合使用

创建于 2018-02-21  ·  49评论  ·  资料来源: apollographql/apollo-link


将 HttpLink 与 node-fetch 一起使用会出现以下错误

import { HttpLink } from 'apollo-link-http';
import fetch from 'node-fetch';

const link = new HttpLink({
      fetch,
      uri: this.endPoint.toString(),
    });

预期结果:
根据文档,上面应该已经编译。

实际结果:

src/tw-algo-manager.ts:20:31 - error TS2345: Argument of type '{ fetch: (url: string | Request, init?: RequestInit | undefined) => Promise<Response>; uri: strin...' is not assignable to parameter of type 'Options | undefined'.
  Type '{ fetch: (url: string | Request, init?: RequestInit | undefined) => Promise<Response>; uri: strin...' is not assignable to type 'Options'.
    Types of property 'fetch' are incompatible.
      Type '(url: string | Request, init?: RequestInit | undefined) => Promise<Response>' is not assignable to type '((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) | undefined'.
        Type '(url: string | Request, init?: RequestInit | undefined) => Promise<Response>' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
          Types of parameters 'url' and 'input' are incompatible.
            Type 'RequestInfo' is not assignable to type 'string | Request'.
              Type 'Request' is not assignable to type 'string | Request'.
                Type 'Request' is not assignable to type 'Request'. Two different types with this name exist, but they are unrelated.
                  Property 'context' is missing in type 'Request'.

20     const link = new HttpLink({
                                 ~21       fetch,
   ~~~~~~~~~~~~22       uri: this.endPoint.toString(),
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~23     }); // const link = createHttpLink(linkOptions);

如何重现问题:
以下版本用于编写上述代码。

    "@types/graphql": "^0.12.4",
    "@types/node-fetch": "^1.6.7",
    "apollo-cache-inmemory": "^1.1.9",
    "apollo-client": "^2.2.5",
    "apollo-link": "^1.2.0",
    "apollo-link-http": "^1.4.0",
    "graphql": "^0.13.1",
    "graphql-tag": "^2.8.0",
    "node-fetch": "^1.7.2",
    "react-apollo": "^2.0.4",
    "url": "^0.11.0"


使用以上版本,在typescript中创建HttpLink的实例,可以看到上面的错误。

最有用的评论

(2018 年 5 月 7 日跟进: cross-fetch现在带有 TypeScript 类型定义)

cross-fetch还没有附带 TypeScript 类型定义(@DefinitelyTyped 也没有)。

等待 lquixada/cross-fetch#12 合并时, cross-fetch是一种替代方法,我发现isomorphic-fetch与 TypeScript 中的HttpLink一起工作正常:

import { HttpLink } from 'apollo-boost'
import fetch from 'isomorphic-fetch'

const link = new HttpLink({
  fetch
})

所有49条评论

如果您使用createHttpLink而不是new HttpLink什么?

import { createHttpLink } from 'apollo-link-http';
import fetch from 'node-fetch';

const link = createHttpLink({
      fetch,
      uri: this.endPoint.toString(),
    });

@dejayc 使用 createHttpLink 时会出现相同的错误。

事实上,我想出了问题。 node-fetch 2.x与 _apollo-link_ 不兼容。 _fetch_ 的签名不同。

node-fetch 2.x 与 apollo-link 不兼容。 fetch 的签名是不同的。

啊,我想你可以躲避它。

我今天在我的同构应用程序中遇到了这个问题。 我不完全同意全局获取是默认的回退,尤其是在 SSR 的这些日子里,但至少这个错误有点信息量:

Error: 
fetch is not found globally and no fetcher passed, to fix pass a fetch for
your environment like https://www.npmjs.com/package/nodefetch.

For example:
import fetch from 'nodefetch';
import { createHttpLink } from 'apollo-link-http';

我的解决方案是根据节点/浏览器环境有条件地使用node-fetchwhatwg-fetch 。 或者只是使用cross-fetch基本上做同样的事情。

所以...

// Using TypeScript
import * as fetch from 'cross-fetch'
new HttpLink({ fetch })

// Or just...
// import 'cross-fetch/polyfill'

@jmca您为 TypeScript 建议的方式对我

我今天遇到了这个问题,发现我们与node-fetch v2 不兼容,我认为我们应该解决这个问题。

我现在开始使用cross-fetch但它只是导入一个fetch: any ...

(2018 年 5 月 7 日跟进: cross-fetch现在带有 TypeScript 类型定义)

cross-fetch还没有附带 TypeScript 类型定义(@DefinitelyTyped 也没有)。

等待 lquixada/cross-fetch#12 合并时, cross-fetch是一种替代方法,我发现isomorphic-fetch与 TypeScript 中的HttpLink一起工作正常:

import { HttpLink } from 'apollo-boost'
import fetch from 'isomorphic-fetch'

const link = new HttpLink({
  fetch
})

npmjs.com 文档鼓励用户使用 node-fetch 但没有指定哪个版本。 我们如何更新该文档?

注意:使用whatwg-fetch是一种可行的解决方法。

这方面有任何动静吗?

使用cross-fetch似乎是一种奇怪的解决方法,因为它只使用node-fetch 。 这真的不是问题吗?

whatwg-fetch不是我们这些服务器端的解决方法。

如果有人打开包含正确类型定义的 PR,该 PR 将适用于所有 fetch 实现但维护一些类型检查,请@stubailo我,我会尝试合并!

@stubailo在 Apollo Server 上,我们将用作apollo-server-env

使用 HttpLink 是可选的,这对我有用:

import ApolloClient from 'apollo-boost'
import 'isomorphic-fetch'

const client = new ApolloClient({
  uri: 'endpoint-url-here'
})

有这方面的消息吗? node-fetch 仍然会导致 apollo-link 和 TS 出现问题。

任何新闻? 仍然遇到node-fetch

对此的解决方法是不安装@types/node-fetch并自己手动将其定义为 GlobalFetch。

为此,请将以下内容添加到项目中的任何.d.ts文件中;

// real node-fetch types clash with apollo-link-http, so manually define it as globalfetch here.
declare module 'node-fetch' {
    const fetch: GlobalFetch['fetch'];
    export default fetch;
}

您可以只使用as类型的强制转换...

当然,但是如果您只是要强制将其强制转换为其他类型,那么安装这些类型有什么意义呢?
在这种情况下,不妨使用我的解决方案。

抱歉,我误读了您在做什么,请忽略我之前的评论。

但是,这需要您修改 tsconfig 以定位浏览器,因为只有在“lib”字段中将“dom”作为条目时才提供 GlobalFetch: https :

其次,不是手动修改.d.ts ,您不能将GlobalFetch['fetch']传递给 HTTPLink 构造函数吗? 这使得它的隐藏性降低了很多。

  1. 当然,但要符合HttpLink 的类型,你必须使用GlobalFetch['fetch']无论如何我认为没有办法绕过这个要求。

  2. 我不跟。 GlobalFetch['fetch']是一个类型,而不是一个变量。
    您的意思是导入 node-fetch 然后将其转换为GlobalFetch['fetch']吗?
    这不是我的选择,因为我在我的项目中启用了noImplictAny ,所以我无法导入任何没有定义的东西。

这只是一种对我有用的解决方法🤷‍♀️,但我意识到它远非完美(关键字:_workaround_)。

我认为一个好的解决方案是将类型更改为GlobalFetch['fetch']并集和 node-fetch 导出的默认类型。
或者只是将推荐的库更改为符合GlobalFetch['fetch'] (whatwg-fetch 或其他)的节点获取库。

  1. 啊,我没有意识到这是必需的。 直到。

  2. 抱歉,我今天真的很闲。 你是对的,那是一种类型,而不是一个变量。

https://www.npmjs.com/package/whatwg-fetch非常明确地指出“该项目在 Node.js 环境下不起作用。它仅适用于 Web 浏览器。您应该确保您的应用程序不会尝试打包并在服务器上运行它。”

是的,有人真的应该通过修复类型来正确修复错误。

进一步查看,这似乎主要是node-fetch ,所以我打开了上面的问题。 然而,他们完全有可能告诉我们改变我们的类型。

好吧, node-fetch似乎在告诉我们改变我们的类型。 一种技巧是导入node-fetch的类型并将其添加为解决方法?

@grantwwu抱歉,GraphQL 峰会的日程安排非常繁忙,但我们最近开始使用apollo-env包,该包以正确的类型重新导出node-fetchhttps://github .com/apollographql/apollo-tooling/tree/master/packages/apollo-env

(不过,我们可能希望将全局提取导出分开)

我刚刚完成了一个import { fetch } from 'apollo-env'并且在将它传递给HttpLink构造函数时我_仍然_得到 TypeScript 错误。

TSError: ⨯ Unable to compile TypeScript:
src/index.ts(20,31): error TS2345: Argument of type '{ uri: string; fetch: (input?: string | Request | undefined, init?: RequestInit | undefined) => Promise<Response>; }' is not assignable to parameter of type 'Options'.
  Types of property 'fetch' are incompatible.
    Type '(input?: string | Request | undefined, init?: RequestInit | undefined) => Promise<Response>' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
      Types of parameters 'input' and 'input' are incompatible.
        Type 'RequestInfo' is not assignable to type 'string | Request | undefined'.
          Type 'Request' is not assignable to type 'string | Request | undefined'.
            Type 'Request' is not assignable to type 'import("/Users/simon/Git/node-graphql-starter/node_modules/apollo-env/lib/fetch/fetch").Request'.
              Types of property 'headers' are incompatible.
                Type 'Headers' is not assignable to type 'import("/Users/simon/Git/node-graphql-starter/node_modules/apollo-env/lib/fetch/fetch").Headers'.
                  Types of property 'values' are incompatible.
                    Type '() => IterableIterator<string>' is not assignable to type '() => Iterator<[string]>'.
                      Type 'IterableIterator<string>' is not assignable to type 'Iterator<[string]>'.
                        Types of property 'next' are incompatible.
                          Type '{ (value?: any): IteratorResult<string>; (value?: any): IteratorResult<string>; }' is not assignable to type '{ (value?: any): IteratorResult<[string]>; (value?: any): IteratorResult<[string]>; }'.
                            Type 'IteratorResult<string>' is not assignable to type 'IteratorResult<[string]>'.
                              Type 'string' is not assignable to type '[string]'.

我设法让它在我的测试中工作:

import { fetch } from 'apollo-env'

......

function httpLink({ apiUrl, idToken }) {
  return new HttpLink({
    uri: apiUrl,
    headers: {
      authorization: `Bearer ${idToken}`,
    },
    fetch
  })
}

它仍然不适合我:/

src/remoteSchemas.ts:54:45 - error TS2322: Type '(input?: RequestInfo, init?: RequestInit) => Promise<Response>' is not assignable to type '(input: RequestInfo, init?: RequestInit) => Promise<Response>'.
  Types of parameters 'input' and 'input' are incompatible.
    Type 'RequestInfo' is not assignable to type 'import("/Users/grant.wu/petuum/api-gateway/node_modules/apollo-env/lib/fetch/fetch").RequestInfo'.
      Type 'Request' is not assignable to type 'RequestInfo'.
        Type 'Request' is not assignable to type 'import("/Users/grant.wu/petuum/api-gateway/node_modules/apollo-env/lib/fetch/fetch").Request'.
          Types of property 'headers' are incompatible.
            Type 'Headers' is missing the following properties from type 'Headers': entries, keys, values, [Symbol.iterator]

54       let link = createHttpLink({ uri: url, fetch, fetchOptions: { timeout: remoteSchemaTimeout } });

@jacobtani您使用了哪个版本的 apollo-http-link 和 apollo-env? 另外,这是与节点,对吗?

是的,这里也一样。 使用 apollo-env 并不能解决我的问题。

Argument of type '{ credentials: string; fetch: (input?: string | Request | undefined, init?: RequestInit | undefined) => Promise<Response>; uri: string; }' is not assignable to parameter of type 'PresetConfig'.
  Types of property 'fetch' are incompatible.
    Type '(input?: string | Request | undefined, init?: RequestInit | undefined) => Promise<Response>' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
      Types of parameters 'input' and 'input' are incompatible.
        Type 'RequestInfo' is not assignable to type 'string | Request | undefined'.
          Type 'Request' is not assignable to type 'string | Request | undefined'.
            Type 'Request' is not assignable to type 'import("/Users/rahul/work/r3pi/vi-image-contours/node_modules/apollo-env/lib/fetch/fetch").Request'.
              Types of property 'headers' are incompatible.
                Type 'Headers' is missing the following properties from type 'Headers': entries, keys, values, [Symbol.iterator]

@grantwwu :我用
"阿波罗客户端": "^2.4.12",
"apollo-env": "^0.3.2",
"apollo-link-http": "^1.5.9",

我在我的应用程序中使用纱线进行依赖管理

我为我的项目切换到了graphql-request

@jacobtani你的 tsconfig 是什么样的?

apollo-env仍然与 HttpLink 期望的不同。 不需要输入参数

我最终像这样覆盖它并且它起作用了

declare module "apollo-env" {
  export function fetch(
    input: RequestInfo,
    init?: RequestInit,
  ): Promise<Response>;
}

我正在使用这个从节点使用这个

"apollo-env": "^0.3.3"
"apollo-link-http": "^1.5.11"

请修补 DOCS APOLLO 团队! 这个问题已经一年多了

@rlancer我认为是这种情况,因为如上所述,您的评论尚未修复,因为apollo-env需要补丁。 这不在这个 repo 中,而是在apollo-tooling存储库中。

@JoviDeCroock有一些

@JoviDeCroock apollo-envapollo-tooling具有相同的提取类型声明,并且都与 HttpLink 期望的 Global['fetch'] 不同。 但这似乎不是问题,如果我用与apollo-env相同的声明自己声明模块,它不会抱怨类型。 也许导出不起作用

好吧,我个人从不使用这些。 如果你能指出我的解决方案,我很乐意公关

我想通过使这两种类型相同应该可以工作。

declare function fetch(
  input?: RequestInfo, ---> remove ?
  init?: RequestInit
): Promise<Response>;

declare interface GlobalFetch {
  fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
}

@jmca谢谢伙计,你救了我的一天,在使用 jest 测试时,在新的 createUploadLink 中出现了问题 self 未定义的问题。

@tafelito感谢您的解决方案,它非常有帮助,但我认为找到了真正的错误,这是由于新的 TypeScript 更新。 在它的 3.6 版本中, GlobalFetch被删除了,而是使用了WindowOrWorkerGlobalScope ,因此这迫使我们关闭 package.json "typescript": "3.5.1"依赖项中的版本。

这是链接

为了回应上述评论,我的回购必须坚持打字稿v3.5.3

我想通过使这两种类型相同应该可以工作。

declare function fetch(
  input?: RequestInfo, ---> remove ?
  init?: RequestInit
): Promise<Response>;

declare interface GlobalFetch {
  fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
}

我已经能够只使用第二部分来修复它,谢谢@tafelito

同样的问题。

"@types/node-fetch": "^2.5.0",
 "typescript": "^3.5.1"
"node-fetch": "^2.6.0",
error TS2345: Argument of type '{ uri: string; fetch: typeof fetch; }' is not assignable to parameter of type 'Options'.
  Types of property 'fetch' are incompatible.
    Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
      Types of parameters 'url' and 'input' are incompatible.
        Type 'RequestInfo' is not assignable to type 'import("/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/@types/node-fetch/index").RequestInfo'.
          Type 'Request' is not assignable to type 'RequestInfo'.
            Type 'Request' is missing the following properties from type 'Request': context, compress, counter, follow, and 6 more.

8 const link = new HttpLink({ uri: 'http://localhost:3000', fetch });

我的应用程序中有同样的问题。 我像这样解决了转换为any

import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-boost'
import { createHttpLink } from 'apollo-link-http'
import fetch from 'node-fetch'

const httpLink = createHttpLink({
//ISSUE: https://github.com/apollographql/apollo-link/issues/513
fetch: fetch as any,
uri: 'https://api.graph.cool/simple/v1/swapi',
})

const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
})

export default client

最终的问题是node-fetch和相关的类型故意偏离规范:

// copied directly from the @types/node-fetch
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node-fetch/index.d.ts
Request {
   ...
    // node-fetch extensions to the whatwg/fetch spec
    agent?: Agent | ((parsedUrl: URL) => Agent);
    compress: boolean;
    counter: number;
    follow: number;
    hostname: string;
    port?: number;
    protocol: string;
    size: number;
    timeout: number;

因此,如果您不向下转型或创建省略这些添加的新类型,则编译器错误是不可避免的。

我选择贬低node-fetch

import nodeFetch from 'node-fetch'
import { WhatWgFetch } from '../src/interfaces' // export type WhatWgFetch = typeof fetch
const fetch = (nodeFetch as unknown) as WhatWgFetch

它不是很好,但是 node-fetch != fetch,这......抱怨......是误导。 node-fetch-like可能更合适,将扩展 _out_ 留在基本节点获取实现中以保持节点获取兼容会对我们这样的人有所帮助:)

这可能完全无关......但是......在最后一天左右遇到了同样的问题......只是意识到'fetch'是浏览器中可用的东西,但不在服务器端。 如果您的应用程序要从浏览器获取数据,那么在服务器端预渲染连接是没有意义的。

就我而言,使用 NextJS,我根据https://nextjs.org/docs#with -no-ssr 更改了需要在服务器端呈现 http-link 的组件

我遇到了同样的问题,我没有安装node-fetch@types/node-fetch而是直接使用apollo-env和 bam! 所有的错误都消失了!

我设法使用 isomorphic-fetch 使其工作

package.json(为与 appsync 使用的版本兼容而选择的所有版本)
    "apollo-link": "1.2.3",
    "apollo-link-context": "1.0.9",
    "apollo-link-http": "1.3.1",
    "aws-appsync": "^3.0.2",
    "isomorphic-fetch": "^2.2.1",
...
    "@types/isomorphic-fetch": "0.0.35",
.../typings/index.d.ts
declare function fetch(
  input?: RequestInfo,
  init?: RequestInit
): Promise<Response>;

declare interface GlobalFetch {
  fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
}
配置文件
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "lib": [ "dom",  "es6",  "esnext.asynciterable" ]
     ...
   },
  "types": [  "node", "aws-sdk"  ],
  "include": [ "./src/**/*.ts" ],
...
代码
import * as fetch from 'isomorphic-fetch';

const client = new AWSAppSyncClient(appSyncClientOptions, {
        link: createAppSyncLink({
          ...appSyncClientOptions,
          resultsFetcherLink: ApolloLink.from([
            createHttpLink({
              fetch: fetch as GlobalFetch['fetch'],
              uri: appSyncClientOptions.url
            })
          ])
        })
      });

考虑cross-fetch

这适用于打字稿和节点(不是浏览器)。

import fetch from 'cross-fetch'

const httpLink = new HttpLink({
  uri: "<your-uri>",
  fetch,
})
此页面是否有帮助?
0 / 5 - 0 等级