Apollo-link-rest: "Headers is not defined" occur when using apollo-link-rest in weixin mini program

Created on 16 Jul 2019  ·  25Comments  ·  Source: apollographql/apollo-link-rest

  • [x] has-reproduction

When we try to use apollo-link-rest in weixin mini program, we encounted a problem which has throw an error as below:

 Headers is not defined 
 ReferenceError: Headers is not defined

As I found the source code in RestLink.ts, it does try to find the Headers, but absolutely, there is no Headers under weixin environment, so can anyone help to provide some hack or solutions to solve this issue? Much appreciate if you could help, thanks.

help wanted 🛠 question❔

Most helpful comment

Here's what ended up working for me:

import clientFetch from 'unfetch';
import serverFetch, { Headers as ServerHeaders } from 'node-fetch';

const client = typeof document !== 'undefined';
global.Headers = client ? global.Headers : ServerHeaders;
const customFetch = client ? clientFetch : serverFetch;

const acmeLink = new JsonApiLink({
  …
  credentials: 'same-origin',
  customFetch,
});

All 25 comments

Hi @Janice1114, I’m unfamiliar with weixin, but maybe you can use a Headers Polyfill?

Often, if Headers is unavailable, Fetch will also be unavailable. So a polyfill that might work for both is this one: https://github.com/bitinn/node-fetch

Hi @fbartho , thanks for your quick reply. I've tried to use the polyfill to enable fetch headers, but still get the errors.

And I found that under weixin, it uses wx.request for the fetch, which does not support window related variable, so I'm afraid the polyfill won't work.

And may I know would there be any hack or implementations to help to skip the headers loop in source code of apollo-link-rest? Or could you please give some custom options to suit under different scenario?

You can use the customFetch parameter to ApolloLinkRest in order to provide a wrapper function, this wrapper function can take the window-based-headers from the Polyfill, and flatten it into a traditional object Hash.

Something like the following (untested, written in my browser):

function customFetch(url, options) {
   const headers = options.headers.entries().reduce((accumulator, h) => { accumulator[h[0]] = h[1];  return accumulator;}, {})
   return fetch(url, {…options, headers});
}
// Elsewhere: new RestLink({ customFetch, ...

So much appreciate for your kindly response, I will try to use this polyfill in weixin development.

And as I read the source code in RestLink.ts, it will try to access the Headers, which is not available in weixin. It could assign the variables in global.

So I wonder if any custom or overrided settings, could be applied during RestLink constructor process?

Best regards again for your so wonderful tips.

Hi @fbartho ,to fix this issue in weixin and related special browser, I've create a PR as below:
Headers issue fix

Could you please help and review whether the code PR is available to merge into master?
Much appreciate and thanks a lot if that could help.

I have the same problem in "next": "^9.0.3"

I'm also having this problem with next, version ^8.1.0. For my graph API I use isomorphic-unfetch, version ^3.0.0 to polyfill like so:

import fetch from 'isomorphic-unfetch'
const graphLink = createHttpLink({ uri, fetch })

But using the same package to polyfill with the rest link did not work:

const restLink = new RestLink({
  uri,
  customFetch: fetch,
})

I also tried node-fetch using your customFetch method:

import fetch from 'node-fetch'

function customFetch(url, options) {
  const headers = options.headers.entries().reduce((accumulator, h) => {
    accumulator[h[0]] = h[1]
    return accumulator
  }, {})
  return fetch(url, { ...options, headers })
}

const restLink = new RestLink({
  uri: CONTENTFUL_CONTENT_DELIVERY_API,
  customFetch,
})

In both cases I got ReferenceError: Headers is not defined when running next in dev mode.

Love the idea here, would love if you could provide some direction for how to implement in server environments. Thanks!

You can polyfill the Headers API while still using your own customFetch API!

Let me know if you would like some sample code there, but I’m confident this is doable without too much hassle. I’m not at my computer just this minute.

@fbartho Some sample code would be great when you have a chance! I took a stab at it, but wasn't successful. Thanks!

import * as Polyfillheaders from 'fetch-headers'
import fetch from 'isomorphic-unfetch'

// hook in the Headers polyfill for your environment!
global.Headers = global.Headers || Polyfillheaders;

function customFetch(url, options) {
  const headers = options.headers.entries().reduce((accumulator, h) => {
    accumulator[h[0]] = h[1]
    return accumulator
  }, {})
  return fetch(url, { ...options, headers })
}

const restLink = new RestLink({
  uri: CONTENTFUL_CONTENT_DELIVERY_API,
  customFetch,
})

Something like the above might do the trick @2wheelcoder

Thanks! I'll report back here after I have a chance to give this a shot.

@fbartho Hi! I'm facing this problem on ios 9.3.5.I tried import the fetch-headers polyfill but not work.
I found that the header instance from fetch-headers doesn't have the forEach method(see below).
image

Is there any other solutions?

@fbartho Hi! I'm facing this problem on ios 9.3.5.I tried import the fetch-headers polyfill but not work.
I found that the header instance from fetch-headers doesn't have the forEach method(see below).
image

Is there any other solutions?

I solved this problem by using the fetch polyfill.

I'm going to close this, because I believe we can resolve this with a polyfill on the App-side, and we don't want to make the polyfill a dependency of this repository.

I have not been able to solve this with a polyfill. I've tried isomorphic-fetch, isomorphic-unfetch, fetch-headers, etc and always end up with an issue about current.forEach not being defined or TypeError: Right-hand side of 'instanceof' is not an object. Any help?

@VinSpee Have you ever tried this fetch polyfill.

@lintuming yes but it doesn’t work on the server.

@VinSpee Maybe you can use this,it seems they have the forEach method.
image
Hope this work.

@lintuming thanks for trying to help. With that one, I get:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

seems to be a tangled web of problems 🤣

@VinSpee Sorry,I have no idea why this happen.But i think this could help:
https://stackoverflow.com/questions/7042340/error-cant-set-headers-after-they-are-sent-to-the-client

Here's what ended up working for me:

import clientFetch from 'unfetch';
import serverFetch, { Headers as ServerHeaders } from 'node-fetch';

const client = typeof document !== 'undefined';
global.Headers = client ? global.Headers : ServerHeaders;
const customFetch = client ? clientFetch : serverFetch;

const acmeLink = new JsonApiLink({
  …
  credentials: 'same-origin',
  customFetch,
});

@VinSpee i am facing same issue and i tried to fix this issue with your solution but i am not succeeded.
Can you please share your working code

Thanks @VinSpee —this was exactly what I needed

I was able to fix this issue by using custom fetch

import fetch from "isomorphic-fetch"

const restLink = new RestLink({
  uri: "https://...",
  customFetch: fetch,
  headers: {
    "Content-Type": "application/json",
  },
})

@VinSpee 's solution worked for me for a Gatsby JS application as well.

Was this page helpful?
0 / 5 - 0 ratings