Skip to content

Instantly share code, notes, and snippets.

@jeremybradbury
Last active March 24, 2022 06:54
Show Gist options
  • Save jeremybradbury/cf049bf7aa6447e284bf07c923601c0d to your computer and use it in GitHub Desktop.
Save jeremybradbury/cf049bf7aa6447e284bf07c923601c0d to your computer and use it in GitHub Desktop.
a JS wrapper for NodeJS http/s Request, supports json & forms via post body
// written for NodeJS v12
const https = require("https"); // core node
const http = require("http"); // core node
const qs = require("querystring"); // core node
const isProd = process.env.NODE_ENV === "production";
const debug = process.env.DEBUG;
const request = async (
url = "", // string
options = {}, // any, or see: https://nodejs.org/api/http.html#new-agentoptions
postData = null, // null | string
onEnd = (data) => Buffer.concat(data).toString(), // (data:array) => string | Buffer | Buffer[]
onData = (chunk, data) => [...data, chunk] // (chunk: string | Buffer, data: string[] | Buffer[]) => string[] | Buffer[]
) => {
// handle https callouts the proper way
options.method = options.method || (postData && "POST" || "GET"); // method is optional, even if options were provided
options.headers = options.headers || {}; // headers are optional, even if options were provided
// json when not specified
if (! options.headers["Content-Type"])
options.headers["Content-Type"] = "application/json";
// handle uploads and forms
if (options.headers["Content-Type"] === "application/x-www-form-urlencoded" )
postData = postData ? qs.stringify(postData) : null; // form body or null
else postData = postData ? JSON.stringify(postData) : null; // json body or null
// handle local requests without config
const httpx = url.startsWith('https') ? https : http; // inline require (one not both) is fast with binary modules at execution time but CJS only
// handle debugging
if (!isProd) console.debug("request url: ", {url, options});
if (!isProd && debug) console.debug({postData}, "\n", {onEnd}, "\n", {onData});
return new Promise((resolve, reject) => {
const req = httpx.request(url, options, (res) => { // class extends Stream interface: https://nodejs.org/api/http.html#class-httpclientrequest
let data = [];
res.on("data", (chunk) => onData(chunk, data)); // this could also handle a stream
res.on("end", () => {
const final = onEnd(data); // this could also handle a stream
if (res.statusCode < 200 || res.statusCode >= 300) {
const parsed = typeof final === string ? JSON.parse(final) : final; // don't fail on non-string handlers
// construct a human readable error message to log and / or respond with
reject(`request(${options.method}): ${res.statusCode} ` + url.split("?")[0]}
+ " " + parsed?.error
? parsed.error + " : " parsed.message
: JSON.stringify(final)
); // note: printing a stack here on response errors isn't helpful
} else {
resolve(final); // return response body as a string (or however you formatted it)
}
});
});
req.on("error", (err) =>
reject(`request(${options.method}): ${err.message}`)); // use try/catch in your implementation
if (postData) {
req.write(postData); // send the post body text
}
req.end();
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment