Make calling REST APIs easier by creating niche-specific
got
instances.
Example: gh-got
Configure a new got
instance with the provided settings. You can access the resolved options with the .defaults
property on the instance.
Note: In contrast to got.extend()
, this method has no defaults.
To inherit from parent, set it as got.defaults.options
or use got.mergeOptions(defaults.options, options)
.
Note: Avoid using object spread as it doesn't work recursively.
Type: boolean
Default: false
States if the defaults are mutable. It's very useful when you need to update headers over time.
Type: Function
Default: undefined
A function making additional changes to the request.
To inherit from parent, set it as got.defaults.handler
.
To use the default handler, just omit specifying this.
Note: These options are normalized.
Returns a Promise
or a Stream
depending on options.stream
.
const settings = {
handler: (options, next) => {
if (options.stream) {
// It's a Stream
// We can perform stream-specific actions on it
return next(options)
.on('request', request => setTimeout(() => request.abort(), 50));
}
// It's a Promise
return next(options);
},
options: got.mergeOptions(got.defaults.options, {
responseType: 'json'
})
};
const jsonGot = got.create(settings);
const defaults = {
options: {
method: 'GET',
retry: {
retries: 2,
methods: [
'GET',
'PUT',
'HEAD',
'DELETE',
'OPTIONS',
'TRACE'
],
statusCodes: [
408,
413,
429,
500,
502,
503,
504
],
errorCodes: [
'ETIMEDOUT',
'ECONNRESET',
'EADDRINUSE',
'ECONNREFUSED',
'EPIPE',
'ENOTFOUND',
'ENETUNREACH',
'EAI_AGAIN'
]
},
headers: {
'user-agent': `${pkg.name}/${pkg.version} (https://github.com/sindresorhus/got)`
},
hooks: {
beforeError: [],
init: [],
beforeRequest: [],
beforeRedirect: [],
beforeRetry: [],
afterResponse: []
},
decompress: true,
throwHttpErrors: true,
followRedirect: true,
stream: false,
form: false,
cache: false,
useElectronNet: false,
responseType: 'text',
resolveBodyOnly: 'false'
},
mutableDefaults: false
};
// Same as:
const defaults = {
handler: got.defaults.handler,
options: got.defaults.options,
mutableDefaults: got.defaults.mutableDefaults
};
const unchangedGot = got.create(defaults);
const settings = {
handler: got.defaults.handler,
options: got.mergeOptions(got.defaults.options, {
headers: {
unicorn: 'rainbow'
}
})
};
const unicorn = got.create(settings);
// Same as:
const unicorn = got.extend({headers: {unicorn: 'rainbow'}});
Got supports composing multiple instances together. This is very powerful. You can create a client that limits download speed and then compose it with an instance that signs a request. It's like plugins without any of the plugin mess. You just create instances and then compose them together.
Merges many instances into a single one:
- options are merged using
got.mergeOptions()
(+ hooks are merged too), - handlers are stored in an array.
Some examples of what kind of instances you could compose together:
const controlRedirects = got.create({
options: got.defaults.options,
handler: (options, next) => {
const promiseOrStream = next(options);
return promiseOrStream.on('redirect', resp => {
const host = new URL(resp.url).host;
if (options.allowedHosts && !options.allowedHosts.includes(host)) {
promiseOrStream.cancel(`Redirection to ${host} is not allowed`);
}
});
}
});
It's very useful in case your machine's got a little amount of RAM.
const limitDownloadUpload = got.create({
options: got.defaults.options,
handler: (options, next) => {
let promiseOrStream = next(options);
if (typeof options.downloadLimit === 'number') {
promiseOrStream.on('downloadProgress', progress => {
if (progress.transferred > options.downloadLimit && progress.percent !== 1) {
promiseOrStream.cancel(`Exceeded the download limit of ${options.downloadLimit} bytes`);
}
});
}
if (typeof options.uploadLimit === 'number') {
promiseOrStream.on('uploadProgress', progress => {
if (progress.transferred > options.uploadLimit && progress.percent !== 1) {
promiseOrStream.cancel(`Exceeded the upload limit of ${options.uploadLimit} bytes`);
}
});
}
return promiseOrStream;
}
});
const noUserAgent = got.extend({
headers: {
'user-agent': null
}
});
const httpbin = got.extend({
baseUrl: 'https://httpbin.org/'
});
const crypto = require('crypto');
const getMessageSignature = (data, secret) => crypto.createHmac('sha256', secret).update(data).digest('hex').toUpperCase();
const signRequest = got.extend({
hooks: {
beforeRequest: [
options => {
options.headers['sign'] = getMessageSignature(options.body || '', process.env.SECRET);
}
]
}
});
If these instances are different modules and you don't want to rewrite them, use got.mergeInstances()
.
Note: The noUserAgent
instance must be placed at the end of chain as the instances are merged in order. Other instances do have the user-agent
header.
const merged = got.mergeInstances(controlRedirects, limitDownloadUpload, httpbin, signRequest, noUserAgent);
(async () => {
// There's no 'user-agent' header :)
await merged('/');
/* HTTP Request =>
* GET / HTTP/1.1
* accept-encoding: gzip, deflate, br
* sign: F9E66E179B6747AE54108F82F8ADE8B3C25D76FD30AFDE6C395822C530196169
* Host: httpbin.org
* Connection: close
*/
const MEGABYTE = 1048576;
await merged('http://ipv4.download.thinkbroadband.com/5MB.zip', {downloadLimit: MEGABYTE});
// CancelError: Exceeded the download limit of 1048576 bytes
await merged('https://jigsaw.w3.org/HTTP/300/301.html', {allowedHosts: ['google.com']});
// CancelError: Redirection to jigsaw.w3.org is not allowed
})();