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

Example how to use with node-fetch? #48

Closed
pkieltyka opened this issue Mar 22, 2020 · 3 comments
Closed

Example how to use with node-fetch? #48

pkieltyka opened this issue Mar 22, 2020 · 3 comments
Labels

Comments

@pkieltyka
Copy link

hi there, does anyone have an example how to use this polyfill combined with node-fetch?

the idea would be to hook up these libraries through a quick mapping..

import * as fetch from 'node-fetch'
import { ReadableStream } from 'web-streams-polyfill/ponyfill/es2018'

fetch.Body.prototype.getReader = function() {
  return new ReadableStream()
}

unfortunately fetch.Body is not exported but perhaps the node-fetch authors would accept a PR to export it so it could be polyfilled..

or any other suggestions?

@MattiasBuelens
Copy link
Owner

MattiasBuelens commented Mar 22, 2020

First, you need a way to convert a Node.js Readable stream into a web ReadableStream. Since a Node stream is an async iterable, you can use its async iterator to build an adapter:

function readableStreamFromAsyncIterable(iterable) {
  const it = iterable[Symbol.asyncIterator]();
  return new ReadableStream({
    async pull(controller) {
      const { done, value } = await it.next();
      if (done) {
        controller.close();
      } else {
        controller.enqueue(value);
      }
    },
    async cancel(reason) {
      await it.throw(reason);
    }
  }, { highWaterMark: 0 });
}

// Usage:
const webStream = readableStreamFromAsyncIterable(nodeStream);

In the future, this whole thing may be replaced by ReadableStream.from(nodeStream), so you no longer need to write your own adapter (see whatwg/streams#1018).

Second, you need to call this adapter on the stream returned by node-fetch Body.body property. You could call it from your own code, or you could "monkey-patch" the Request.prototype and Response.prototype:

import { Readable } from 'stream';

function getBodyAsWebStream() {
  const body = this.body;
  if (body instanceof Readable) {
    return readableStreamFromAsyncIterable(body);
  }
  // If you also want to support a Buffer body:
  if (body instanceof Buffer) {
    return new ReadableStream({
      start(controller) {
        controller.enqueue(body);
        controller.close();
      }
    })
  }
  return null;
}
fetch.Request.prototype.bodyAsWebStream = getBodyAsWebStream;
fetch.Response.prototype.bodyAsWebStream = getBodyAsWebStream;

Note that monkey-patching a third-party library can lead to compatibility issues with future versions of that library, so I wouldn't recommend it.

unfortunately fetch.Body is not exported but perhaps the node-fetch authors would accept a PR to export it so it could be polyfilled..

From looking at their code, that wouldn't work. Their Response class does not "extend" from Body, instead all properties and methods from Body are "mixed in" the Response class. So even if Body were exported, the Body.prototype object wouldn't be part of a Response object's prototype chain... 😕

If you really want to monkey-patch, it seems that you'll need to patch both Request.prototype and Response.prototype.

@jimmywarting
Copy link

btw, there is an todo in node-fetch to remove the Buffer from the body and replacing it with a stream. so that this piece becomes unnecessary.

  // If you also want to support a Buffer body:
  if (body instanceof Buffer) {
    return new ReadableStream({
      start(controller) {
        controller.enqueue(body);
        controller.close();
      }
    })
  }

@pkieltyka
Copy link
Author

thank you both, that helps a lot

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

No branches or pull requests

3 participants