Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
1 task done
Janice1114 opened this issue Jul 16, 2019 · 28 comments
Closed
1 task done

Comments

@Janice1114
Copy link

Janice1114 commented Jul 16, 2019

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

@fbartho
Copy link
Collaborator

fbartho commented Jul 16, 2019

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

@Janice1114
Copy link
Author

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?

@fbartho
Copy link
Collaborator

fbartho commented Jul 18, 2019

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

@Janice1114
Copy link
Author

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.

@leoswing
Copy link

leoswing commented Jul 28, 2019

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.

@rasovica
Copy link

rasovica commented Aug 9, 2019

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

@2WheelCoder
Copy link

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!

@fbartho
Copy link
Collaborator

fbartho commented Aug 13, 2019

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.

@2WheelCoder
Copy link

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

@fbartho
Copy link
Collaborator

fbartho commented Aug 13, 2019

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

@2WheelCoder
Copy link

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

@Lam9090
Copy link

Lam9090 commented Nov 5, 2019

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

@Lam9090
Copy link

Lam9090 commented Nov 5, 2019

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

@fbartho
Copy link
Collaborator

fbartho commented Nov 5, 2019

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.

@VinSpee
Copy link

VinSpee commented Jan 10, 2020

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?

@Lam9090
Copy link

Lam9090 commented Jan 10, 2020

@VinSpee Have you ever tried this fetch polyfill.

@VinSpee
Copy link

VinSpee commented Jan 10, 2020

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

@Lam9090
Copy link

Lam9090 commented Jan 10, 2020

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

@VinSpee
Copy link

VinSpee commented Jan 10, 2020

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

@Lam9090
Copy link

Lam9090 commented Jan 10, 2020

@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

@VinSpee
Copy link

VinSpee commented Jan 10, 2020

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

@venkataluri
Copy link

@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

@gtwright
Copy link

Thanks @VinSpee —this was exactly what I needed

@vikas26294
Copy link

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

@wlockiv
Copy link

wlockiv commented Sep 14, 2020

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

@damiangreen
Copy link

damiangreen commented Apr 25, 2022

I'm using crossfetch, , but it complained about the Headers object, I tried to use fetch-headers with it. but with no luck. (Type 'typeof import("..node_modules/fetch-headers/headers") | { new (init?: HeadersInit): Headers; prototype: Headers; }' is not assignable to type '{ new (init?: HeadersInit): Headers; prototype: Headers; }'. Property 'prototype' is missing in type 'typeof import("..i/node_modules/fetch-headers/headers")' but required in type '{ new (init?: HeadersInit): Headers; prototype: Headers; }'.ts(2322) )
I'm hesitant to include node-fetch alongside crossfetch to get this working

@fbartho
Copy link
Collaborator

fbartho commented Apr 25, 2022

Hey @damiangreen — do you have a gist or a full snippet of the code you’re using for poly filling the headers?

This is a situation where you need the runtime to have a Headers instance, TypeScript is just being grumpy about some prototype-best practices (I think it’s an extra-strict TypeScript language version).

Anyhow, your code should look something like:

import { Headers as HeadersPolyfill } from “my-headers-polyfill-source”

global.Headers = (HeadersPolyfill as any); // ‘as any’ to bypass the ‘prototype’ warning


const restLink = new RestLink();

@ashleybartlett
Copy link

I was able to use a custom polyfill modified to work for typescript, as described in the node-fetch readme

// fetch-polyfill.ts
import fetch, {
  Blob,
  blobFrom,
  blobFromSync,
  File,
  fileFrom,
  fileFromSync,
  FormData,
  Headers as FetchHeaders,
  Request as FetchRequest,
  Response as FetchResponse,
} from "node-fetch";

declare global {
    var Headers: typeof FetchHeaders;
    var Request: typeof FetchRequest;
    var Response: typeof FetchResponse;
}

if (!globalThis.Headers) {
  globalThis.Headers = FetchHeaders;
  globalThis.Request = FetchRequest;
  globalThis.Response = FetchResponse;
}

Then from somewhere, early in your code

// index.ts
import 'fetch-polyfill.ts';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests