cheap node.js ‘fetch’ for people not wanting to use 3rd party libs

Using javascript/typescript ? rest ? using json ? Just http(s) >= 1.1 ?

more and more people are getting tired with the idea of bundling external libraries in their own service / library.
Can I just call another endpoint without adding ‘fetch’, ‘axios’ or anything ? Adding them is automatically enabling the burden of maintaining a package-lock file + future upgrades + git bots to keep everything up to date.
My purpose is just to have the smallest possible JS bundle.

So I have done a budget version of fetch with the just the relevant and meaningful code, able to fetch data from a remote endpoint.
To make it compatible with isomorphic-fetch, an async json() function exists to collect the result as a json object.

The result is a drop-in replacement for that library, but also for window.fetch or deno’s fetch function.
Even with this small snippet, you already get the request + the response + the decompression of the response body. And of course, everything here uses built-in node.js capabilities.

import { IncomingMessageIncomingHttpHeaders } from “http”;
import { requestRequestOptions } from “https”;
import { URL } from “url”;
import { gunzipinflatebrotliDecompress } from “zlib”;
export const fetch = async (
  urlURL | string,
  options?: RequestOptions & { body?: Buffer | string | nullmethod?: string }
): Promise<{
  statusCode?: number;
  headersIncomingHttpHeaders;
  json: () => Promise<any>;
}> => {
  const urlObject = url instanceof URL ? url : new URL(“”url);
  const [responserequestError]: [
    IncomingMessage | null,
    Error | null
  ] = await new Promise((resolve=> {
    const http1RequestOptionsRequestOptions = {
      …(options || {}),
      hostname: urlObject.hostname,
      path: url
        .toString()
        .replace(/https?:\/\//“”)
        .replace(/^[^/]*/i“”),
      port: urlObject.port,
      protocol: urlObject.protocol,
      rejectUnauthorized: false,
      method: options?.method,
      headers: {
        …(options?.headers ?? {}),
        host: urlObject.hostname,
      },
    };
    const outboundHttp1Request = request(http1RequestOptions, (res=>
      resolve([resnull])
    );
    if (options?.body)
      outboundHttp1Request.write(
        typeof options.body === “object”
          ? JSON.stringify(options.body)
          : options.body
      );
    outboundHttp1Request.on(“error”, (thrown=> {
      resolve([nullthrown]);
    });
    outboundHttp1Request.end();
  });
  if (requestErrorthrow requestError;
  const [dataresponseError]: [
    Buffer | null,
    Error | null
  ] = await new Promise((resolve=> {
    let partialBody = Buffer.alloc(0);
    response?.on(“error”, (thrown=> {
      resolve([nullthrown]);
    });
    response?.on(“data”, (message=> {
      partialBody = Buffer.concat([partialBodymessage]);
    });
    response?.on(“end”, () => {
      resolve([partialBodynull]);
    });
  });
  if (responseErrorthrow responseError;
  const fetchResponse = async () =>
    await (response?.headers[“content-encoding”] || “”)
      .split(“,”)
      .reduce((bufferformatNotTrimed=> {
        const format = formatNotTrimed.trim().toLowerCase();
        const method =
          format === “gzip” || format === “x-gzip”
            ? gunzip
            : format === “deflate”
            ? inflate
            : format === “br”
            ? brotliDecompress
            : format === “identity” || format === “”
            ? (
                inputBuffer,
                callback: (err?: Errordata?: Buffer=> void
              ) => {
                callback(undefinedinput);
              }
            : null;
        if (method === null)
          throw new Error(`${format} compression not supported by the proxy`);
        return buffer.then(
          (data1=>
            new Promise((resolve=>
              !method
                ? resolve(data1)
                : (method as any)(data1, (err?: Errordata2?: Buffer=> {
                    if (errthrow err;
                    resolve(data2!);
                  })
            )
        );
      }, Promise.resolve(data));
  return {
    …response!,
    json: () =>
      fetchResponse().then((data=>
        !data ? null : JSON.parse(data.toString())
      ),
  };
};

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.