Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Using node-fetch with apollo-link-http #513

Open
yogeshsajanikar opened this issue Feb 21, 2018 · 49 comments · May be fixed by #1271
Open

Using node-fetch with apollo-link-http #513

yogeshsajanikar opened this issue Feb 21, 2018 · 49 comments · May be fixed by #1271

Comments

@yogeshsajanikar
Copy 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.

@dejayc
Copy link

dejayc commented Feb 23, 2018

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(),
    });

@yogeshsajanikar
Copy link
Author

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

@dejayc
Copy link

dejayc commented Feb 23, 2018

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.

@jmca
Copy link

jmca commented Feb 24, 2018

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'

@julianguyen
Copy link

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

@huan
Copy link

huan commented Mar 7, 2018

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

@wongmjane
Copy link

wongmjane commented Apr 26, 2018

(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
})

@gardner
Copy link

gardner commented Apr 29, 2018

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.

@grantwwu
Copy link

grantwwu commented Jul 27, 2018

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.

@stubailo
Copy link
Contributor

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!

@martijnwalraven
Copy link
Contributor

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

@jaaaco
Copy link

jaaaco commented Aug 24, 2018

using HttpLink is optional, this works for me:

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

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

@moimael
Copy link

moimael commented Sep 17, 2018

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

@XBeg9
Copy link

XBeg9 commented Oct 30, 2018

Any news? Still getting issues with node-fetch

@jariz
Copy link

jariz commented Nov 1, 2018

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

@grantwwu
Copy link

grantwwu commented Nov 1, 2018

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

@jariz
Copy link

jariz commented Nov 1, 2018

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.

@grantwwu
Copy link

grantwwu commented Nov 1, 2018

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: #273 (comment)

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.

@jariz
Copy link

jariz commented Nov 1, 2018

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

@grantwwu
Copy link

grantwwu commented Nov 1, 2018

  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.

@grantwwu
Copy link

grantwwu commented Nov 1, 2018

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.

@grantwwu
Copy link

grantwwu commented Nov 6, 2018

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?

@martijnwalraven
Copy link
Contributor

@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

@martijnwalraven
Copy link
Contributor

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

@Siyfion
Copy link

Siyfion commented Dec 12, 2018

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

@jacobtani
Copy link

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

@grantwwu
Copy link

grantwwu commented Feb 8, 2019

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?

@rahulthewall
Copy link

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]

@jacobtani
Copy link

@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

@rahulthewall
Copy link

I switched to graphql-request for my project.

@grantwwu
Copy link

@jacobtani What does your tsconfig look like?

@tafelito
Copy link

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"

@rlancer
Copy link

rlancer commented Feb 25, 2019

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

@JoviDeCroock
Copy link
Contributor

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

@rlancer
Copy link

rlancer commented Feb 25, 2019

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

@tafelito
Copy link

@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

@JoviDeCroock
Copy link
Contributor

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

@tafelito
Copy link

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

@hariantara
Copy link

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

@jddiazs
Copy link

jddiazs commented Aug 30, 2019

@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, GlobalFetch is removed and WindowOrWorkerGlobalScope is 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

@chenrui333
Copy link

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

@lewislbr
Copy link

lewislbr commented Sep 4, 2019

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

@mrdulin
Copy link

mrdulin commented Sep 7, 2019

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

@wagnerlduarte
Copy link

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

@cdaringe
Copy link

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

@techedemic
Copy link

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

@saminnet
Copy link

saminnet commented Dec 9, 2019

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!

@mcroker
Copy link

mcroker commented Dec 17, 2019

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

@jjangga0214
Copy link

jjangga0214 commented Feb 1, 2020

Consider cross-fetch !

This works with typescript and node(not browser).

import fetch from 'cross-fetch'

const httpLink = new HttpLink({
  uri: "<your-uri>",
  fetch,
})

jjangga0214 added a commit to jjangga0214/apollo-link that referenced this issue May 3, 2020
@jjangga0214 jjangga0214 linked a pull request May 3, 2020 that will close this issue
hwillson added a commit to apollographql/apollo-client that referenced this issue May 15, 2020
`node-fetch` types aren't fully compatible with AC3, leading to
TS compiler errors in some cases (for the full backstory, see
apollographql/apollo-link#513).
`cross-fetch` addresses these type issues (while still using
`node-fetch` under the hood), and also supports older browsers
as well. This means we can recommend the use of `cross-fetch`
instead of both `unfetch` and `node-fetch`. This PR updates our
docs and code checks accordingly.
benjamn pushed a commit to apollographql/apollo-client that referenced this issue May 18, 2020
The `node-fetch` types aren't fully compatible with AC3, leading to
TS compiler errors in some cases (for the full backstory, see
apollographql/apollo-link#513).
`cross-fetch` addresses these type issues (while still using
`node-fetch` under the hood), and also supports older browsers
as well. This means we can recommend the use of `cross-fetch`
instead of both `unfetch` and `node-fetch`. This PR updates our
docs and code checks accordingly.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.