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

request-compose

v2.1.7

Published

Composable HTTP Client

Downloads

790,678

Readme

request-compose

npm-version test-ci-img test-cov-img snyk-vulnerabilities

Composable HTTP Client

var compose = require('request-compose')
var Request = compose.Request
var Response = compose.Response

;(async () => {
  try {
    var {res, body} = await compose(
      Request.defaults({headers: {'user-agent': 'request-compose'}}),
      Request.url('https://api.github.com/users/simov'),
      Request.send(),
      Response.buffer(),
      Response.string(),
      Response.parse(),
    )()
    console.log(res.statusCode, res.statusMessage)
    console.log(res.headers['x-ratelimit-remaining'])
    console.log(body)
  }
  catch (err) {
    console.error(err)
  }
})()

Goals

  • No dependencies
  • No abstraction
  • No state

Table of Contents

Compose

In computer science, function composition (not to be confused with object composition) is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.

var compose = require('request-compose')

Accepts a list of functions to execute and returns a Promise:

var doit = compose(
  (a) => a + 2,
  (a) => a * 2,
)

Then we can call it:

var result = await doit(5) // 14

A more practical example however would be to compose our own HTTP client:

var compose = require('request-compose')
var https = require('https')

var request = compose(
  (options) => {
    options.headers = options.headers || {}
    options.headers['user-agent'] = 'request-compose'
    return options
  },
  (options) => new Promise((resolve, reject) => {
    https.request(options)
      .on('response', resolve)
      .on('error', reject)
      .end()
  }),
  async (res) => await new Promise((resolve, reject) => {
    var body = ''
    res
      .on('data', (chunk) => body += chunk)
      .on('end', () => resolve({res, body}))
      .on('error', reject)
  }),
  ({res, body}) => ({res, body: JSON.parse(body)}),
)

Then we can use it like this:

;(async () => {
  try {
    var {res, body} = await request({
      protocol: 'https:',
      hostname: 'api.github.com',
      path: '/users/simov',
    })
    console.log(res.statusCode, res.statusMessage)
    console.log(res.headers['x-ratelimit-remaining'])
    console.log(body)
  }
  catch (err) {
    console.error(err)
  }
})()

Bundled Middlewares

request-compose comes with a bunch of pre-defined middlewares for transforming the request and the response:

var compose = require('request-compose')
var Request = compose.Request
var Response = compose.Response

We can use these middlewares to compose our own HTTP client:

;(async () => {
  try {
    var {res, body} = await compose(
      Request.defaults({headers: {'user-agent': 'request-compose'}}),
      Request.url('https://api.github.com/users/simov'),
      Request.send(),
      Response.buffer(),
      Response.string(),
      Response.parse(),
    )()
    console.log(res.statusCode, res.statusMessage)
    console.log(res.headers['x-ratelimit-remaining'])
    console.log(body)
  }
  catch (err) {
    console.error(err)
  }
})()

| Type | Middleware | Input | Arguments | Returns | :--- | :--- | :--- | :--- | :--- | Request | defaults | {input} | {input} | {options} | Request | url, proxy, qs, cookie | see options | {options} | {options} | Request | form, json, multipart, body | see options | {options} | {options, body} | Request | auth, oauth | see options | {options, body} | {options, body} | Request | length | - | {options, body} | {options, body} | Request | send | - | {options, body} | {options, res} | Response | buffer | - | {options, res} | {options, res, body} | Response | gzip | - | {options, res, body, raw} | {options, res, body, raw} | Response | string | see options | {options, res, body, raw} | {options, res, body, raw} | Response | parse, status | - | {options, res, body, raw} | {options, res, body, raw} | Response | redirect | (input, client) | {options, res, body, raw} | new composition

Opinionated Client

request-compose comes with opinionated HTTP client that is composed of the above middlewares.

There are 3 types of composition available based on the returned data type:

client

var request = require('request-compose').client
var {res, body} = await request({options})

The client composition does the following:

  • buffers the response body
  • decompresses gzip and deflate encoded bodies with valid content-encoding header
  • converts the response body to string using utf8 encoding by default
  • tries to parse JSON and querystring encoded bodies with valid content-type header

Returns either String or Object.

buffer

var request = require('request-compose').buffer
var {res, body} = await request({options})

The buffer composition does the following:

  • buffers the response body
  • decompresses gzip and deflate encoded bodies with valid content-encoding header

Returns Buffer.

stream

var request = require('request-compose').stream
var {res} = await request({options})

The stream composition returns the response Stream.

options

The above compositions accept any of the Node's http.request and https.request options:

var {res, body} = await request({
  method: 'GET',
  url: 'https://api.github.com/users/simov',
  headers: {
    'user-agent': 'request-compose'
  }
})

Additionally the following options are available:

| Option | Type | Description | :-- | :-- | :-- | url | 'string' url object | URL (encoding - see below) | proxy | 'string' url object | Proxy URL | qs | {object} 'string' | URL querystring (encoding - see below) | form | {object} 'string' | application/x-www-form-urlencoded request body (encoding - see below) | json | {object} 'string' | JSON encoded request body | multipart| {object} [array] | multipart request body using request-multipart, see examples | body | 'string' Buffer Stream | request body | auth | {user, pass} | Basic authorization | oauth | {object} | OAuth 1.0a authorization using request-oauth, see examples | encoding | 'string' | response body encoding (default: 'utf8') | cookie | {object} | cookie store using request-cookie, see examples | redirect | {object} | see below

Querystring set in the url, and/or in qs and/or in form as 'string' is left untouched, meaning that the proper encoding is left to the user.

When qs and/or form is {object} the querystring is encoded using the Node's querystring module which mirrors the global encodeURIComponent method. Additionally all reserved characters according to RFC3986 are encoded as well. Full list of all reserved characters that are being encoded can be found here.

redirect

| Option | Default | Description | :-- | :-- | :-- | max | 3 | maximum number of redirects to follow | all | false | follow non-GET HTTP 3xx responses as redirects | method | true | follow original HTTP method, otherwise convert all redirects to GET | auth | true | keep Authorization header when changing hostnames | referer | false | add Referer header

extend

Extend or override any of the bundled request and response middlewares:

var request = require('request-compose').extend({
  Request: {
    oauth: require('request-oauth'),
    multipart: require('request-multipart'),
    cookie: require('request-cookie').Request
  },
  Response: {cookie: require('request-cookie').Response},
}).client

Errors

Non 200/300 responses are thrown as Error object with the following properties:

  • message - status code + status message
  • res - the response object
  • body - the parsed response body
  • raw - the raw response body

Debug Logs

Fancy request-logs:

npm i --save-dev request-logs

Pick any of the following debug options:

DEBUG=req,res,body,json,nocolor node app.js

Examples

| Topic | Example | :-- | :-- | Basics | | Types of lambda functions | Get GitHub user profile | Bundled middlewares | Get GitHub user profile | Wrap it up and extend it | Get GitHub user profile | Compositions | | Client | Get GitHub user profile | Buffer | Decoding response body using iconv-lite | Stream | Stream Tweets | External Middlewares | | OAuth (request-oauth) | Get Twitter User Profile | Multipart (request-multipart) | Upload photo to Twitter | Cookie (request-cookie) | Login to Wallhaven.cc | Stream | | Stream request body | Upload file to Dropbox | HTTP stream | Upload image from Dropbox to Slack | HTTP stream | Copy file from Dropbox to GDrive | Misc | | Gzip decompression | Request Gzip compressed body | HTTPS proxy | Tunnel Agent | HTTPS proxy | Proxy Agent | Override bundled middleware - per compose instance | Override the qs middleware | Override bundled middleware - process-wide | Override the form and the parse middlewares to use the qs module | Pipeline | | App pipeline | Slack Weather Status | App pipeline | Simultaneously search for repos in GitHub, GitLab and BitBucket | Modules | | Google Chrome Web Store HTTP Client | chrome-webstore | REST API Client Library | purest