Apollo-link: Using node-fetch with apollo-link-http

Created on 21 Feb 2018  ·  49Comments  ·  Source: apollographql/apollo-link


Using HttpLink with node-fetch gives following error

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

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

Intended outcome:
According to the documentation the above should have compiled.

Actual outcome:

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);

How to reproduce the issue:
Following versions are used to write above code.

    "@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"


Use the above versions and create the instance of HttpLink in typescript to see the above error.

Most helpful comment

(Follow up on May 7, 2018: cross-fetch now comes with TypeScript type definitions)

cross-fetch has not shipped with TypeScript type definitions yet (nor does @DefinitelyTyped).

While waiting for lquixada/cross-fetch#12 to be merged cross-fetch is an alternative, I found isomorphic-fetch working alright with HttpLink in TypeScript as of now:

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

const link = new HttpLink({
  fetch
})

All 49 comments

What happens if you use createHttpLink instead of new HttpLink?

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

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

@dejayc It gives same error when createHttpLink is used.

Actually, I figured out the problem. The node-fetch 2.x is not compatible with _apollo-link_. The signature of _fetch_ is different.

The node-fetch 2.x is not compatible with apollo-link. The signature of fetch is different.

Ah, I guess you can duck punch it.

I came across this issue today in my isomorphic app. I don't totally agree with global fetch being the default fallback especially in these days of SSR, but at least the error was somewhat informative:

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';

My solution was either to conditionally usenode-fetch or whatwg-fetch depending on the node/browser environment. Or just use cross-fetch which basically does the same thing.

So...

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

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

@jmca The way you suggested for TypeScript worked for me! Thanks!!

I ran into this issue today, and find out we are not compatible with node-fetch v2, I believe we should fix this.

I start using cross-fetch now but it just imports a fetch: any...

(Follow up on May 7, 2018: cross-fetch now comes with TypeScript type definitions)

cross-fetch has not shipped with TypeScript type definitions yet (nor does @DefinitelyTyped).

While waiting for lquixada/cross-fetch#12 to be merged cross-fetch is an alternative, I found isomorphic-fetch working alright with HttpLink in TypeScript as of now:

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

const link = new HttpLink({
  fetch
})

The npmjs.com documentation encourages users to use node-fetch but doesn't specify which version. How can we update that documentation?

Note: using whatwg-fetch is a viable workaround.

Has there been any movement on this?

Using cross-fetch seems like a strange workaround since it just uses node-fetch. Is that not actually a problem?

whatwg-fetch is not a workaround for those of us on the server side.

If someone opens a PR that includes the right type definitions that will work with all of the fetch implementations but maintains some type checking, please @stubailo me and I'll try to merge!

@stubailo On Apollo Server we use this as part of apollo-server-env.

using HttpLink is optional, this works for me:

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

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

Any news on this ? node-fetch still causes issues with apollo-link and TS.

Any news? Still getting issues with node-fetch

A workaround for this is to not install @types/node-fetch and manually define it as GlobalFetch yourself.

To do this, add the following to any .d.ts file in your project;

// 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;
}

You can just use an as type coercion thing...

Sure but what is the point of installing the types at all, if you're just gonna force cast it to something else eitherway?
Might as well use my solution in that case.

Sorry, I misread what you were doing, ignore my previous comment.

However, this requires you modify your tsconfig to target the browser, as GlobalFetch is only provided if you have "dom" as an entry in the "lib" field: https://github.com/apollographql/apollo-link/issues/273#issuecomment-347878009

Secondly, instead of manually modifying a .d.ts, couldn't you just pass GlobalFetch['fetch'] to the HTTPLink constructor? That makes it a lot less hidden.

  1. Sure, but to conform to HttpLink's typing you have to use GlobalFetch['fetch'] regardless so I see no way around that requirement.

  2. I don't follow. GlobalFetch['fetch'] is a type, not a variable.
    Do you mean importing node-fetch and then casting it to GlobalFetch['fetch']?
    That's not on option for me as I have noImplictAny enabled in my projects so I can't import anything that doesn't have a definition.

It's just a workaround that works for me 🤷‍♀️, but I realise it's far from perfect (keyword: _workaround_).

I think a good solution would be to just change the typing to a union of GlobalFetch['fetch'] and the default type of node-fetch's export.
Or just change the recommended library to a node fetch lib that DOES conform to GlobalFetch['fetch'] (whatwg-fetch or whatever).

  1. Ah, I didn't realize that was required. TIL.

  2. Sorry, I'm just having a really off day. You're right, that's a type, not a variable.

https://www.npmjs.com/package/whatwg-fetch states pretty explicitly that "This project doesn't work under Node.js environments. It's meant for web browsers only. You should ensure that your application doesn't try to package and run this on the server."

Yes, someone really ought to fix the bug properly by fixing the types.

Upon further review, this seems to be mainly a problem with node-fetch, so I opened the above issue. It's entirely possible that they will tell us to just change our types, however.

Okay, node-fetch seems to be telling us to just change our types. One hack would be to import node-fetch's types and to add that as a workaround?

@grantwwu Sorry, things are pretty hectic in the rundown to GraphQL Summit, but we've recently started using an apollo-env package that re-exports node-fetch with the right types: https://github.com/apollographql/apollo-tooling/tree/master/packages/apollo-env

(We'll probably want to separate out the global fetch exports though)

I've just done an import { fetch } from 'apollo-env' and I'm _still_ getting TypeScript errors when passing it into the HttpLink constructor.

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]'.

I managed to get this to work in my test:

import { fetch } from 'apollo-env'

......

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

It's still not working for me :/

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 which versions of apollo-http-link and apollo-env did you use? Also, this is with node, right?

Yes, same here. Using apollo-env does not solve the issue for me.

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 : I use
"apollo-client": "^2.4.12",
"apollo-env": "^0.3.2",
"apollo-link-http": "^1.5.9",

I use yarn for dependency management in my app

I switched to graphql-request for my project.

@jacobtani What does your tsconfig look like?

apollo-env is still different from what HttpLink expects. the input param should not be required

I ended up overriding it like this and it worked

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

I'm using this from node using this

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

PLEASE PATCH THE DOCS APOLLO TEAM! This issue is over a year old

@rlancer I assume this is the case because as mentioned above your comment, it is not fixed yet since apollo-env needs a patch. This is not in this repo but in the apollo-tooling repository.

@JoviDeCroock There are solutions that work, docs should indicate to use them as opposed to one that fails and forces people to Google for workarounds.

@JoviDeCroock both apollo-env and apollo-tooling has the same fetch types declaration and both are different from the Global['fetch'] that the HttpLink expects. But this doesn't seem to be the problem, if I declare the module myself with the same declaration as apollo-env it does not complain about types. Maybe the export is not working

Well, I personally never use these. If you can point me to a solution I'd gladly pr

I guess by making these 2 types the same should work.

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

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

@jmca Thanks man, you saved my day, got head bang got problem self is undefined in new createUploadLink while using jest testing.

@tafelito Thanks for your solution, it was very helpful, but I think the real error was found and it is due to the new TypeScript update. In its 3.6 version, GlobalFetchis removed and WindowOrWorkerGlobalScopeis used instead, so this forces us to close the version in our dependencies of the package.json "typescript": "3.5.1".

Here is the link

To echo the above comment, my repo has to stick with typescript v3.5.3

I guess by making these 2 types the same should work.

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

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

I've been able to fix it using only the second part, thanks @tafelito

same issue.

"@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 });

I have same problem in my application. I solve casting to any like that:

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

ultimately the problem is that the node-fetch & associated typings intentionally deviate from the spec:

// 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;

so compiler errors are inevitable if you don't downcast or create new typings that omit these additions.

i chose to downcast node-fetch:

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

it's not great, but node-fetch != fetch, which ...grumbles... is misleading. node-fetch-like may have been more appropriate, and leaving the extensions _out_ in the base node-fetch implementation to keep node-fetch compliant would have done the likes of us a favor :)

This might be completely irrelevant ... but ... had the same issue for the last day or so...only to realize that 'fetch' is something that is available in the browser, but not on the server-side. If your application is going to be doing fetching from the browser, it's kind of pointless to pre-render the connection on the server-side.

In my case, using NextJS, I changed the component that needed the http-link to not be rendered on the server-side as per https://nextjs.org/docs#with-no-ssr

I was having the same problem, instead of installing node-fetch and @types/node-fetch I went straightly with apollo-env and bam! all errors gone!

I managed to get this working with isomorphic-fetch

package.json (all version selected for compatibility with that used by 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>;
}
tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "lib": [ "dom",  "es6",  "esnext.asynciterable" ]
     ...
   },
  "types": [  "node", "aws-sdk"  ],
  "include": [ "./src/**/*.ts" ],
...
code
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
            })
          ])
        })
      });

Consider cross-fetch !

This works with typescript and node(not browser).

import fetch from 'cross-fetch'

const httpLink = new HttpLink({
  uri: "<your-uri>",
  fetch,
})
Was this page helpful?
0 / 5 - 0 ratings