npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

decorquest

v0.0.4

Published

Decorators for Node's core request modules

Downloads

7

Readme

decorquest

Decorators for Node's core request modules, to extend it's functionality.

rant

I'll start with a rant as substack did in hyperquest.

request is the most popular module for doing http requests and web scraping. My biggest problem with this module is that it tries to take care of everything related to http requests.

This fact yields to a codebase with several thousands lines of code. It violates SOLID principles. It's not extandable. And it full of bugs.

If you're doing an http request here and there, you should use request.

However, if you're doing massive web scraping, or just doing several hundred API request, request would broke. And it will break your app.

Trust me...

The smallest error caused my CPU to spin.

hyperquest on the other hand, is scalable. It can handle big amount of requests. However, it lacks on extensibility. I was using hyperquest until I had a need to add some extensions. I was able to write 2 modules that can extend hyperquest, however I couldn't achieve connection tunneling.

And that was my motive to write decorquest.

How it works?

In my point of view, node modules should be super small. Node modules should be extensible and allow others to extend them.

That's how decorquest works. It follows the decorator design pattern, with some adaptations for node. This module contains several small components to extend Node's http/https.request functionality on demand.

Components

request

This is the basic request function. It follows the API of Node's http/https request. And the only thing it does, is to choose between http or https for request.

In order to make an https request, you must have {'protocol': 'https:'} in opts. The semicolon is on purpose so that you'll be able to pass `url.parse("https://some-https-url.com") as options.

disableGlobalAgent

This is a decorator for the core request. It's goal is to disable http.globalAgent if no agent was specified. If you do specify an agent in ops, it will work as the core module.

Usage:

var url = require("url");
var dq = require("decorquest");
var request = dq.disableGlobalAgent(dq.request);

var r = request(url.parse("http://www.bing.com"), function (res) {
  res.pipe(process.stdout);
}
r.end();

attachAuthorizationHeaders

This decorator attaches the Authorization header if auth is present in opts.

Usage:

var url = require("url");
var dq = require("decorquest");
var request = dq.attachAuthorizationHeaders(dq.disableGlobalAgent(dq.request));

var r = request(url.parse("http://user:[email protected]"), function (res) {
  res.pipe(process.stdout);
}
r.end();

cookiequest

This decorators takes care of cookies if jar is present in opts. It will store cookies in the CookieJar and will send the stored cookies in followup requests.

If the jar property is set to true, it will use a global jar that is attached to this decorator. If you want to use a different CookieJar for every request, you can attach a CookieJar object that is provided by cookiejar module.

Example with the default CookieJar:

var url = require("url");
var dq = require("decorquest");
var request = cookiequest(dq.attachAuthorizationHeaders(dq.disableGlobalAgent(dq.request)));

var opts = url.parse("http://www.bing.com");
opts.jar = true;

var r = request(opts, function (res) {
  res.pipe(process.stdout);
}
r.end();

Example with a CookieJar object:

var url = require("url");
var dq = require("decorquest");
var request = cookiequest(dq.attachAuthorizationHeaders(dq.disableGlobalAgent(dq.request)));

var cookiejar = new (require("cookiejar").CookieJar)();

var opts = url.parse("http://www.bing.com");
opts.jar = cookiejar;

var r = request(opts, function (res) {
  res.pipe(process.stdout);
}
r.end();

proxyquest

This decorator brings the functionality of using http proxy over http request. It will passthrough all requests with different protocols. In order to use it, you have to specify proxy in opts.

Usage:

var url = require("url");
var dq = require("decorquest");
var request = proxyquest(cookiequest(dq.attachAuthorizationHeaders(dq.disableGlobalAgent(dq.request))));

var opts = url.parse("http://www.bing.com");
opts.jar = true;
opts.proxy = 'http://1:[email protected]:8888'; // This is Fiddler with Fiddler's default `auth`

var r = request(opts, function (res) {
  res.pipe(process.stdout);
}
r.end();

tunnelquest

This decorator completes proxyquest. If you want to scrape https urls over http proxy, this component is for you.

tunnelquest uses the tunnel module. It will override the agent in opts to the tunneling agent provided by tunnel.

This module will passthrough in case proxy wasn't specified or protocol isn't https:.

Usage:

var url = require("url");
var dq = require("decorquest");
var request = tunnelquest(proxyquest(cookiequest(dq.attachAuthorizationHeaders(dq.disableGlobalAgent(dq.request)))));

var opts = url.parse("https://www.bing.com"); // The scheme is HTTPS.
opts.jar = true;
opts.proxy = 'http://1:[email protected]:8888'; // This is Fiddler with Fiddler's default `auth`

var r = request(opts, function (res) {
  res.pipe(process.stdout);
}
r.end();

timeoutquest

This decorator will emit an ETIMEDOUT error, and will abort the request. The timeout option is at opts.responseTimeout.

Usage:

var http = require("http");
var url = require("url");
var dq = require("decorquest");
var request = dq.timeoutquest(dq.request);

var server = http.createServer(function (req, res) {
  setTimeout( function () {
    res.write("hello world" + '\n');
    res.end();
  }, 3000);
});


server.listen(5000, function () {
  var opts = url.parse("http://localhost:5000/");
  opts.responseTimeout = 1000;
  var r = request(opts);
  r.end();
  r.on('response', function (res) {
    res.pipe(process.stdout);
    res.on('end', function () {
      server.close();
    });
  });

  r.on('error', function (err) {
    console.error(err);
  })

  r.on('close', function () {
    server.close();
  })
})

Final example

var dq = require("decorquest");
var url = require("url")
var request = dq.proxyquest(dq.disableGlobalAgent(dq.cookiequest(dq.request)));

// Let's create a request DTO
var opts = url.parse("http://www.bing.com");

// Instruct `cookiequest` to use the default Cookie Jar
opts.jar = true;

// Setting my Fiddler proxy. Comment out this line if you're not using fiddler.
opts.proxy = "http://127.0.0.1:8888";

// Let's make a request to bing....
var r = request(opts, function (res) {
  console.log("The cookies that Bing sets\n", res.headers['set-cookie']);

  // Let's make another request
  var r2 = request(opts, function(res) {
    console.log("\n\nHere are the headers that we're sending Bing on the second request\n", res.req._headers);
  });
  r2.end();
});
r.end();

How to write your own decorators

Let's create a useful decorator. Let's call it errorquest. It will help us with error handling and debugging using Node's domains.

var dq = require("../decorquest");
var url = require("url")

/*
  This decorator will help us with Error Handling. It will use Node's error handling with `domains` functionality.
 */
function errorquest(request) {
  return function(opts, cb) {
    // Let's passthrough if it doesn't have the `_domain` property
    if (!opts._domain) return request(opts, cb);

    var d = opts._domain;
    var r = request(opts, cb);

    // Let's add the Request object to the domain.
    d.add(r);

    // Let's detach it when the request object ends or got closed
    r.on("end", function () { d.remove(r); });
    r.on("close", function () { d.remove(r); });

    // Let's bind to it's socket as well
    r.on("socket", function (socket) {
      d.add(socket);

      // Remove it when it's done
      socket.on("end", function () { d.remove(socket); });
      socket.on("close", function () { d.remove(socket); });
    });

    return r;
  }
}

// Create the request object
var request = errorquest(dq.proxyquest(dq.disableGlobalAgent(dq.cookiequest(dq.request))));

// Create the domain
var d = require("domain").create();

// Let's see what errors we'll get
d.on("error", function (err) {
  console.log(err.stack);
})

// Make a request that will fail
var opts = url.parse("http://this-domain-will-fail.js");
opts._domain = d;

// Action!!!
var r = request(opts, function () {
  //Nothing to do here
});
r.end();

// And we get:
/*
 Error: getaddrinfo ENOTFOUND
 at errnoException (dns.js:37:11)
 at Object.onanswer [as oncomplete] (dns.js:124:16)
 */

install

With npm do:

npm install decorquest

license

MIT