Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
request-compose
Advanced tools
The request-compose npm package is a versatile tool for making HTTP requests and composing them in a functional manner. It allows for chaining requests, handling responses, and managing errors in a streamlined way.
Making HTTP Requests
This feature allows you to make HTTP requests using a simple and intuitive API. The example demonstrates how to make a GET request to an API endpoint and log the response body.
const { compose } = require('request-compose');
const { Request } = compose;
(async () => {
const { res, body } = await Request({
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'GET'
});
console.log(body);
})();
Chaining Requests
This feature allows you to chain multiple HTTP requests together. The example demonstrates making a GET request followed by a POST request, showing how to handle multiple requests in sequence.
const { compose } = require('request-compose');
const { Request } = compose;
(async () => {
const { res: res1, body: body1 } = await Request({
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'GET'
});
const { res: res2, body: body2 } = await Request({
url: 'https://jsonplaceholder.typicode.com/posts',
method: 'POST',
body: { title: 'foo', body: 'bar', userId: 1 },
json: true
});
console.log(body2);
})();
Error Handling
This feature provides robust error handling for HTTP requests. The example demonstrates how to catch and handle errors when a request fails.
const { compose } = require('request-compose');
const { Request } = compose;
(async () => {
try {
const { res, body } = await Request({
url: 'https://jsonplaceholder.typicode.com/invalid-url',
method: 'GET'
});
console.log(body);
} catch (error) {
console.error('Request failed:', error.message);
}
})();
Axios is a popular promise-based HTTP client for the browser and Node.js. It provides a simple API for making HTTP requests and supports features like interceptors, request cancellation, and automatic JSON transformation. Compared to request-compose, Axios is more widely used and has a larger community, but it may not offer the same level of composability.
Node-fetch is a lightweight module that brings the Fetch API to Node.js. It is minimalistic and focuses on providing a fetch-like interface for making HTTP requests. While it is simpler and more lightweight than request-compose, it lacks the advanced composability and chaining features.
Superagent is a small, progressive client-side HTTP request library that also works in Node.js. It provides a flexible API for making HTTP requests and supports features like query string parsing, form submissions, and file uploads. Superagent is more feature-rich than node-fetch but may not offer the same level of composability as request-compose.
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)
}
})()
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)
}
})()
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 |
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:
var request = require('request-compose').client
var {res, body} = await request({options})
The client
composition does the following:
gzip
and deflate
encoded bodies with valid content-encoding
headerutf8
encoding by defaultJSON
and querystring
encoded bodies with valid content-type
headerReturns either String or Object.
var request = require('request-compose').buffer
var {res, body} = await request({options})
The buffer
composition does the following:
gzip
and deflate
encoded bodies with valid content-encoding
headerReturns Buffer.
var request = require('request-compose').stream
var {res} = await request({options})
The stream
composition returns the response Stream.
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 inqs
and/or inform
as 'string' is left untouched, meaning that the proper encoding is left to the user.
When
qs
and/orform
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.
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 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
Non 200/300
responses are thrown as Error object with the following properties:
message
- status code + status messageres
- the response objectbody
- the parsed response bodyraw
- the raw response bodyFancy request-logs:
npm i --save-dev request-logs
Pick any of the following debug options:
DEBUG=req,res,body,json,nocolor node app.js
FAQs
Composable HTTP Client
The npm package request-compose receives a total of 151,727 weekly downloads. As such, request-compose popularity was classified as popular.
We found that request-compose demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.