Technical Documentation: sindresorhus/got
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`readme.md`)
**Fast remote container builds and GitHub Actions runners.**
</a>
<hr>Human-friendly and powerful HTTP request library for Node.js
See how Got compares to other HTTP libraries
You probably want Ky instead, by the same people. It's smaller, works in the browser too, and is more stable since it's built on Fetch. Or fetch-extras for simple needs.
Support questions should be asked here.
Install
npm install gotWarning: This package is native ESM and no longer provides a CommonJS export. If your project uses CommonJS, you will have to convert to ESM. Please don't open issues for questions regarding CommonJS / ESM.
Got v11 is no longer maintained and we will not accept any backport requests.
Take a peek
A quick start guide is available.
JSON mode
Got has a dedicated option for handling JSON payload.
Furthermore, the promise exposes a .json<T>() function that returns Promise<T>.
import got from 'got';
const {data} = await got.post('https://httpbin.org/anything', {
json: {
hello: 'world'
}
}).json();
console.log(data);
//=> {"hello": "world"}For advanced JSON usage, check out the parseJson and stringifyJson options.
For more useful tips like this, visit the Tips page.
Highlights
Documentation
By default, Got will retry on failure. To disable this option, set options.retry.limit to 0.
Main API
Timeouts and retries
Advanced creation
Cache, Proxy and UNIX sockets
Integration
Migration guides
Got plugins
got4aws- Got convenience wrapper to interact with AWS v4 signed APIsgh-got- Got convenience wrapper to interact with the GitHub APIgl-got- Got convenience wrapper to interact with the GitLab APIgotql- Got convenience wrapper to interact with GraphQL using JSON-parsed queries instead of stringsgot-fetch- Got with afetchinterfacegot-scraping- Got wrapper specifically designed for web scraping purposesgot-ssrf- Got wrapper to protect server-side requests against SSRF attacks
Comparison
got |
node-fetch |
ky |
axios |
superagent |
|
|---|---|---|---|---|---|
| HTTP/2 support | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | :heavy_check_mark:** |
| Browser support | :x: | :heavy_check_mark:* | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Promise API | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Stream API | :heavy_check_mark: | Node.js only | :x: | :x: | :heavy_check_mark: |
| Pagination API | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Request aborting | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| RFC 7234 caching | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Cookies (out-of-the-box) | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Follows redirects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Retries on failure | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: |
| Progress events | :heavy_check_mark: | :x: | :heavy_check_mark: | Browser only | :heavy_check_mark: |
| Handles gzip/deflate | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Advanced timeouts | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Timings | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Errors with metadata | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| JSON mode | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Custom defaults | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| Composable | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| Hooks | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| Issues open | |||||
| Issues closed | |||||
| Downloads | |||||
| Coverage | TBD | ||||
| Build | |||||
| Bugs | |||||
| Dependents | |||||
| Install size | |||||
| GitHub stars | |||||
| TypeScript support | |||||
| Last commit |
* It's almost API compatible with the browser fetch API.
** Need to switch the protocol manually. Doesn't accept PUSH streams and doesn't reuse HTTP/2 sessions.
:sparkle: Almost-stable feature, but the API may change. Don't hesitate to try it out!
:grey_question: Feature in early stage of development. Very experimental.
Click here to see the install size of the Got dependencies.
Maintainers
| Sindre Sorhus | Szymon Marczak |
Documentation 1 Promise (`documentation/1-promise.md`)
Promise API
Source code: source/as-promise/index.ts
The main Got function returns a Promise.
Request aborting is supported via the signal option and AbortController.
`got(url: string | URL, options?: [OptionsInit](typescript.md#optionsinit), defaults?: [Options](2-options.md))`
Returns: Promise<[Response](response.md)>
The most common way is to pass the URL as the first argument, then the options as the second.
import got from 'got';
const {headers} = await got(
'https://httpbin.org/anything',
{
headers: {
foo: 'bar'
}
}
).json();`promise.json()`
Returns: Promise<T>
A shortcut method that gives a Promise returning a JSON object.
It is semantically the same as setting options.resolveBodyOnly to true and options.responseType to 'json'.
`promise.buffer()`
Returns: Promise
A shortcut method that gives a Promise returning a Uint8Array.
It is semantically the same as setting options.resolveBodyOnly to true and options.responseType to 'buffer'.
`promise.text()`
Returns: Promise<string>
A shortcut method that gives a Promise returning a string.
It is semantically the same as setting options.resolveBodyOnly to true and options.responseType to 'text'.
`promise.on(event, handler)`
The events are the same as in Stream API.
The response event fires once, with the response the promise resolves with. It does not fire for a response that is retried, or for an HTTPError response when throwHttpErrors is true. A response listener that throws rejects the promise with that error.
`promise.once(event, handler)`
Registers a one-time listener for events from Stream API.
`promise.off(event, handler)`
Removes listener registered with promise.on.
import {createReadStream} from 'node:fs';
import got from 'got';
const ongoingRequestPromise = got.post(uploadUrl, {
body: createReadStream('sample.txt')
});
const eventListener = (progress: Progress) => {
console.log(progress);
};
ongoingRequestPromise.on('uploadProgress', eventListener);
setTimeout(() => {
ongoingRequestPromise.off('uploadProgress', eventListener);
}, 500);
await ongoingRequestPromise;Documentation 10 Instances (`documentation/10-instances.md`)
Instances
Source code: source/create.ts
`got.defaults`
`options`
Type: Options
The options used for this instance.
`handlers`
Type: Handler[]
(options: Options, next: …) => next(options)An array of handlers. The next function returns a Promise or a Request Got stream.
You execute them directly by calling got(…). They are some sort of "global hooks" - these functions are called first. The last handler (it's invisible) is either asPromise or asStream, depending on whether got() or got.stream() was called.
`mutableDefaults`
Type: boolean
Default: false
Determines whether got.defaults.options can be modified.
[!NOTE]
Immutable defaults reject writes. Assigning to a frozen option throws, and readingsearchParamsreturns a copy, so writing to that copy is discarded. Usegot.extend(…)or the request itself to setsearchParams.
`got.extend(…options, …instances)`
Tip:
optionscan includehandlersandmutableDefaults.
Note:
- Properties that are not enumerable, such as
body,json, andform, will not be merged. Pass them to each request instead of togot.extend(): an instance created with one keeps it in its recorded options, sogot.extend(instance, …)can throw when another argument sets a different kind of payload.
Configure a new got instance with merged default options. The options are merged with the parent instance's defaults.options using options.merge(…).
import got from 'got';
const client = got.extend({
prefixUrl: 'https://httpbin.org',
headers: {
'x-foo': 'bar'
}
});
const {headers} = await client.get('headers').json();
console.log(headers['x-foo']); //=> 'bar'
const jsonClient = client.extend({
responseType: 'json',
resolveBodyOnly: true,
headers: {
'x-lorem': 'impsum'
}
});
const {headers: headers2} = await jsonClient.get('headers');
console.log(headers2['x-foo']); //=> 'bar'
console.log(headers2['x-lorem']); //=> 'impsum'Note:
- Handlers can be asynchronous and can return a
Promise, but never aPromise<Stream>whenoptions.isStreamistrue(set internally forgot.stream()requests).- Streams must always be handled synchronously.
- In order to perform async work using streams, the
beforeRequesthook should be used instead.
The recommended approach for creating handlers that can handle both promises and streams is:
import got from 'got';
// Create a non-async handler, but we can return a Promise later.
const handler = (options, next) => {
// Internally set for requests created via `got.stream()`.
if (options.isStream) {
// It's a Stream, return synchronously.
return next(options);
}
// For asynchronous work, return a Promise.
return (async () => {
try {
const response = await next(options);
response.yourOwnProperty = true;
return response;
} catch (error) {
// Every error will be replaced by this one.
// Before you receive any error here,
// it will be passed to the `beforeError` hooks first.
// Note: this one won't be passed to `beforeError` hook. It's final.
throw new Error('Your very own error.');
}
})();
};
const instance = got.extend({handlers: [handler]});Documentation 2 Options (`documentation/2-options.md`)
Options
Source code: source/core/options.ts
Like fetch stores the options in a Request instance, Got does so in Options.
It is made of getters and setters that provide fast option normalization and validation.
By default, Got will retry on failure. To disable this option, set options.retry to {limit: 0}.
Merge behavior explained
When an option is already set, setting it again replaces it with a deep clone by default.
Otherwise the merge behavior is documented in the corresponding section for the option.
How to store options
The Options class is useful for storing and validating configuration for Got instances.
The constructor - new Options(url, options, defaults) - takes the same arguments as the got function.
To use an Options instance, create an extended Got instance:
import got, {Options} from 'got';
const options = new Options({
prefixUrl: 'https://httpbin.org',
headers: {
foo: 'foo'
}
});
options.headers.foo = 'bar';
// Use got.extend() to create an instance with the Options
const instance = got.extend(options);
const {headers} = await instance('anything').json();
console.log(headers.foo);
//=> 'bar'For most use cases, plain objects are simpler and more convenient:
import got from 'got';
const options = {
prefixUrl: 'https://httpbin.org',
headers: {
foo: 'foo'
}
};
options.headers.foo = 'bar';
// Plain objects can be passed directly as the second argument
const {headers} = await got('anything', options).json();
console.log(headers.foo);
//=> 'bar'Note that the Options constructor throws immediately when an invalid option is provided, such as a non-existing option or a typo. With plain objects, validation only happens when the request is made.
For TypeScript users, got exports a dedicated type called OptionsInit.
It is a plain object that can store the same properties as Options.
The Options class is useful for storing the base configuration of a custom Got client, especially when you want early validation of options.
Resetting options
Resetting behavior depends on the option. Scalar options such as responseType and resolveBodyOnly ignore undefined and preserve the parent value. To restore their defaults, explicitly pass responseType: 'text' or resolveBodyOnly: false.
The following options support explicit resets:
instance(…, {searchParams: undefined});
instance(…, {cookieJar: undefined});
instance(…, {responseType: 'text', resolveBodyOnly: false});
instance(…, {prefixUrl: ''});
instance(…, {agent: {http: undefined, https: undefined, http2: undefined}});
instance(…, {context: {token: undefined, …}});
instance(…, {https: {rejectUnauthorized: undefined, …}});
instance(…, {cacheOptions: {immutableMinTimeToLive: undefined, …}});
instance(…, {headers: {'user-agent': undefined, …}});
instance(…, {timeout: {request: undefined, …}});In order to reset hooks, retry and pagination, another Got instance must be created:
const defaults = new Options();
const secondInstance = instance.extend({mutableDefaults: true});
secondInstance.defaults.options.hooks = defaults.hooks;
secondInstance.defaults.options.retry = defaults.retry;
secondInstance.defaults.options.pagination = defaults.pagination;`url`
Type: string | [URL](https://nodejs.org/api/url.html#url_the_whatwg_url_api)
The URL to request. Usually the url represents a WHATWG URL.
Pass it as the first argument to got(url, options). Passing url in an options object is not supported.
import got from 'got';
// This:
await got('https://httpbin.org/anything');
// is semantically the same as this:
await got(new URL('https://httpbin.org/anything'));[!NOTE]
Throws if no protocol specified.
[!NOTE]
Ifurlis a string, then thequerystring will not be parsed as search params.
This is in accordance to the specification.
If you want to pass search params instead, use thesearchParamsoption below.
import got from 'got';
await got('https://httpbin.org/anything?query=a b'); //=> ?query=a%20b
await got('https://httpbin.org/anything', {searchParams: {query: 'a b'}}); //=> ?query=a+b
// The query string is overridden by `searchParams`
await got('https://httpbin.org/anything?query=a b', {searchParams: {query: 'a b'}}); //=> ?query=a+b[!NOTE]
Leading slashes are disallowed to enforce consistency and avoid confusion.
For example, when the prefix URL ishttps://example.com/fooand the input is/bar, there's ambiguity whether the resulting URL would becomehttps://example.com/foo/barorhttps://example.com/bar. The latter is used by browsers.
`searchParams`
Type: string | [URLSearchParams](https://nodejs.org/api/url.html#url_class_urlsearchparams) | object<string, [Primitive](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)>
WHATWG URL Search Params to be added to the request URL.
import got from 'got';
const response = await got('https://httpbin.org/anything', {
searchParams: {
hello: 'world',
foo: 123
}
}).json();
console.log(response.args);
//=> {hello: 'world', foo: 123}If you need to pass an array, you can do it using a URLSearchParams instance:
import got from 'got';
const searchParams = new URLSearchParams([['key', 'a'], ['key', 'b']]);
await got('https://httpbin.org/anything', {searchParams});
console.log(searchParams.toString());
//=> 'key=a&key=b'[!NOTE]
This will override thequerystring inurl.
[!NOTE]
nullvalues are not stringified, an empty string is used instead.undefinedvalues clear the keys inherited from the instance defaults. Thequerystring inurlis still overridden.
**Merge behavior:**
- Overrides existing properties.
`prefixUrl`
Type: string
Default: ''
The string to be prepended to relative string url input.
The prefix must be an absolute URL, since Got has no base to resolve it against. It must not contain a query string or a fragment.
A trailing slash / is optional - one will be added automatically.
import got from 'got';
// This:
const instance = got.extend({prefixUrl: 'https://httpbin.org'});
await instance('anything');
// is semantically the same as this:
await got('https://httpbin.org/anything');[!NOTE]
ChangingprefixUrlalso updates theurloption if set.
[!NOTE]
Absolute string URLs andURLinstances bypassprefixUrlby default. Other instance defaults, includingheaders, still apply. If those URLs may come from untrusted input, setallowAbsoluteUrlstofalse.
[!NOTE]
Got cannot know which custom headers are sensitive. If you use headers likex-api-key, only pass trusted URLs or useallowAbsoluteUrls: false.
`allowAbsoluteUrls`
Type: boolean
Default: true
Allow absolute URLs to bypass prefixUrl.
When set to false with prefixUrl, passing an absolute URL will throw. This also rejects scheme-relative URL strings like //example.com/path in retry and pagination URL overrides. Use this when untrusted URL input must stay on the same origin as the configured prefixUrl. This is not a path sandbox: relative paths like ../other still follow standard URL resolution on the same origin.
import got from 'got';
const client = got.extend({
prefixUrl: 'https://api.example.com',
allowAbsoluteUrls: false,
headers: {
'x-api-key': process.env.API_KEY
}
});
await client('users/1');
// Requests https://api.example.com/users/1
await client('https://attacker.example');
// ThrowsSet prefixUrl to an empty string for a request that intentionally needs an absolute URL:
await client('https://trusted.example', {prefixUrl: ''});[!NOTE]
This guards theurlyou pass. It does not block cross-origin redirects issued by the server, though inherited sensitive headers are still stripped when a redirect changes origin.
[!NOTE]
The check is defeated if the same hook orpagination.paginate(…)return also setsprefixUrlorallowAbsoluteUrls. Do not populate those options from untrusted data.
`signal`
Type: AbortSignal
You can abort the request using AbortController.
import got from 'got';
const abortController = new AbortController();
const request = got('https://httpbin.org/anything', {
signal: abortController.signal
});
setTimeout(() => {
abortController.abort();
}, 100);`method`
Type: string
Default: GET
The HTTP method used to make the request.
Common methods include: GET, HEAD, POST, PUT, DELETE.
Got also supports QUERY, which is defined in RFC 10008.
Custom methods such as PROPFIND are supported. Method names are normalized to uppercase.
import got from 'got';
const {method} = await got('https://httpbin.org/anything', {
method: 'POST'
}).json();
console.log(method);
// => 'POST'`headers`
Type: object<string, string | string[] | number>
Default: {'user-agent': 'got (https://github.com/sindresorhus/got)'}
The HTTP headers to be sent. Headers set to undefined will be omitted. Number values are stored and sent as strings.
import got from 'got';
const {headers} = await got.post('https://httpbin.org/anything', {
headers: {
hello: 'world'
}
}).json();
console.log(headers);
// => {hello: 'world'}**Merge behavior:**
- Overrides existing properties.
`body`
Type: string | ArrayBufferView | stream.Readable | Generator | AsyncGenerator | Iterable | AsyncIterable | FormData
The payload to send.
For string and ArrayBufferView types (including Uint8Array, other typed arrays, and DataView), the content-length header is automatically set if the content-length and transfer-encoding headers are missing.
The content-length header is not automatically set when body is an instance of fs.createReadStream().
To set content-length for file streams, you need to manually provide it using fs.promises.stat():
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import got from 'got';
const filePath = 'path/to/file';
const fileStats = await fsPromises.stat(filePath);
const fileStream = fs.createReadStream(filePath);
await got.post('https://httpbin.org/anything', {
body: fileStream,
headers: {
'content-length': fileStats.size.toString()
}
});import got from 'got';
const {data} = await got.post('https://httpbin.org/anything', {
body: 'Hello, world!'
}).json();
console.log(data);
//=> 'Hello, world!'You can also use typed arrays (Uint8Array, Uint16Array, etc.) as request body:
import got from 'got';
const uint8Body = new Uint8Array([104, 101, 108, 108, 111]); // 'hello' in ASCII
const {data} = await got.post('https://httpbin.org/anything', {
body: uint8Body
}).json();
console.log(data);
//=> 'hello'You can use Iterable and AsyncIterable objects as request body, including Web ReadableStream:
import got from 'got';
// Using an async generator
async function* generateData() {
yield 'Hello, ';
yield 'world!';
}
await got.post('https://httpbin.org/anything', {
body: generateData()
});You can use FormData objects as request body:
import got from 'got';
const form = new FormData();
form.set('greeting', 'Hello, world!');
const data = await got.post('https://httpbin.org/post', {
body: form
}).json();
console.log(data.form.greeting);
//=> 'Hello, world!'[!NOTE]
Ifbodyis specified, then thejsonorformoption cannot be used.
[!NOTE]
If you use this option,got.stream()will be read-only.
[!NOTE]
PassingbodywithGETwill throw unless theallowGetBodyoption is set totrue.
[!NOTE]
This option is not enumerable and will not be merged with the instance defaults.
`json`
Type: JSON-serializable values
JSON request body. If set, the content-type header defaults to application/json.
[!IMPORTANT]
This option only affects the request body you send to the server. To parse the response as JSON, you must either call.json()on the promise or setresponseType: 'json'in the options.
import got from 'got';
const {data} = await got.post('https://httpbin.org/anything', {
json: {
hello: 'world'
}
}).json();
console.log(data);
//=> `{hello: 'world'}``form`
Type: object<string, [Primitive](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)>
The form body is converted to a query string using (new URLSearchParams(form)).toString().
If set, the content-type header defaults to application/x-www-form-urlencoded.
import got from 'got';
const {data} = await got.post('https://httpbin.org/anything', {
form: {
hello: 'world'
}
}).json();
console.log(data);
//=> 'hello=world'`parseJson`
Type: (text: string) => unknown
Default: (text: string) => JSON.parse(text)
The function used to parse JSON responses.
import got from 'got';
import {parse} from '@hapi/bourne';
// Preventing prototype pollution by using Bourne
const parsed = await got('https://example.com', {
parseJson: text => parse(text)
}).json();
console.log(parsed);`stringifyJson`
Type: (object: unknown) => string
Default: (object: unknown) => JSON.stringify(object)
The function used to stringify the body of JSON requests.
Example: ignore all properties starting with an underscore
import got from 'got';
await got.post('https://example.com', {
stringifyJson: object => JSON.stringify(object, (key, value) => {
if (key.startsWith('_')) {
return;
}
return value;
}),
json: {
some: 'payload',
_ignoreMe: 1234
}
});Example: all numbers as strings
import got from 'got';
await got.post('https://example.com', {
stringifyJson: object => JSON.stringify(object, (key, value) => {
if (typeof value === 'number') {
return value.toString();
}
return value;
}),
json: {
some: 'payload',
number: 1
}
});`allowGetBody`
Type: boolean
Default: false
Set this to true to allow sending body for the GET method.
[!NOTE]
This option is only meant to interact with non-compliant servers when you have no other choice.
[!NOTE]
The RFC 7231 doesn't specify any particular behavior for the GET method having a payload, therefore it's considered an anti-pattern.
`copyPipedHeaders`
Type: boolean
Default: false
Automatically copy headers from piped streams.
When piping a request into a Got stream (e.g., request.pipe(got.stream(url))), this controls whether headers from the source stream are automatically merged into the Got request headers.
Note: Explicitly set headers take precedence over piped headers. Piped headers are only copied when a header is not already explicitly set.
Useful for proxy scenarios when explicitly enabled. Got automatically omits host, authorization, cookie, cookie2, set-cookie, set-cookie2, hop-by-hop headers, and headers nominated by Connection/Proxy-Connection. Got cannot know which app-specific headers are sensitive. Leave copyPipedHeaders disabled and copy only safe headers manually, or explicitly omit those headers before piping. If you trust the upstream and want to forward credentials, pass them explicitly in headers.
Example: Opt in to automatic header copying for proxy scenarios
import got from 'got';
import {pipeline} from 'node:stream/promises';
server.get('/proxy', async (request, response) => {
const gotStream = got.stream('https://example.com', {
copyPipedHeaders: true,
// Explicit headers win over piped headers.
// Add credentials here only when the upstream is trusted.
headers: {
host: 'example.com',
}
});
await pipeline(request, gotStream, response);
});Example: Keep it disabled and manually copy only safe headers
import got from 'got';
import {pipeline} from 'node:stream/promises';
server.get('/proxy', async (request, response) => {
const gotStream = got.stream('https://example.com', {
headers: {
'user-agent': request.headers['user-agent'],
'accept': request.headers['accept'],
// Explicitly NOT copying host, connection, authorization, etc.
}
});
await pipeline(request, gotStream, response);
});`timeout`
Type: object
See the Timeout API.
**Merge behavior:**
- Overrides existing properties.
`retry`
Type: object
See the Retry API.
**Merge behavior:**
- Overrides existing properties.
`hooks`
Type: object
See the Hooks API.
**Merge behavior:**
- Merges arrays via
[...hooksArray, ...next]
`encoding`
Type: string
Default: undefined, which decodes the response as UTF-8
Encoding to be used on setEncoding of the response data.
To get a Uint8Array, you need to set responseType to 'buffer' instead. Don't set this option to null.
A leading byte order mark is removed when decoding UTF-8 and UTF-16LE text, for responseType: 'text', responseType: 'json', the .text() and .json() shortcuts, and the default pagination transform. rawBody always keeps the original bytes.
import got from 'got';
const response = await got('https://httpbin.org/anything', {
encoding: 'base64'
}).text();
console.log(response);
//=> base64 string**Note:**
- This option does not affect streams! Instead, do:
import got from 'got';
const stream = got.stream('https://httpbin.org/anything');
stream.setEncoding('base64');
stream.on('data', console.log);`responseType`
Type: 'text' | 'json' | 'buffer'
Default: 'text'
The parsing method.
The promise also has .text(), .json() and .buffer() methods which return another Got promise for the parsed body.
It's like setting the options to {responseType: 'json', resolveBodyOnly: true} but without affecting the main Got promise.
import got from 'got';
const responsePromise = got('https://httpbin.org/anything');
const bufferPromise = responsePromise.buffer();
const jsonPromise = responsePromise.json();
const [response, buffer, json] = await Promise.all([responsePromise, bufferPromise, jsonPromise]);
// `response` is an instance of Got Response
// `buffer` is an instance of Uint8Array
// `json` is an object[!NOTE]
When using streams, this option is ignored.
[!NOTE]
'buffer'will return the raw body bytes as aUint8Array. Any modifications will also alter the result of.text()and.json(). Before overwriting it, please copy it first vianew Uint8Array(buffer).
See https://github.com/nodejs/node/issues/27080
`resolveBodyOnly`
Type: boolean
Default: false
If true, the promise will return the Response body instead of the Response object.
import got from 'got';
const url = 'https://httpbin.org/anything';
// This:
const body = await got(url).json();
// is semantically the same as this:
const body = await got(url, {responseType: 'json', resolveBodyOnly: true});`context`
Type: object<string, unknown>
Default: {}
Note:
- Non-enumerable properties inside are not merged.
Contains user data. It's very useful for storing auth tokens:
import got from 'got';
const instance = got.extend({
hooks: {
beforeRequest: [
options => {
if (typeof options.context.token !== 'string') {
throw new Error('Token required');
}
options.headers.token = options.context.token;
}
]
}
});
const context = {
token: 'secret'
};
const {headers} = await instance('https://httpbin.org/headers', {context}).json();
console.log(headers);
//=> {token: 'secret', …}This option is enumerable. In order to define non-enumerable properties inside, do the following:
import got from 'got';
const context = {};
Object.defineProperties(context, {
token: {
value: 'secret',
enumerable: false,
configurable: true,
writable: true
}
});
const instance = got.extend({context});
console.log(instance.defaults.options.context);
//=> {}**Merge behavior:**
- Overrides existing properties.
`cookieJar`
Type: object | [tough.cookieJar](https://github.com/salesforce/tough-cookie#cookiejar)
**Note:**
- Setting this option will result in the
cookieheader being overwritten.
Cookie support. Handles parsing and storing automatically.
import got from 'got';
import {CookieJar} from 'tough-cookie';
const cookieJar = new CookieJar();
await cookieJar.setCookie('foo=bar', 'https://example.com');
await got('https://example.com', {cookieJar});`cookieJar.setCookie`
Type: (rawCookie: string, url: string) => void | Promise<void>
See ToughCookie API for more information.
`cookieJar.getCookieString`
Type: (currentUrl: string) => string | Promise<string>
See ToughCookie API for more information.
`ignoreInvalidCookies`
Type: boolean
Default: false
Ignore invalid cookies instead of throwing an error.
Only useful when the cookieJar option has been set.
**Note:**
- This is not recommended! Use at your own risk.
`followRedirect`
Type: boolean | (response: PlainResponse) => boolean
Default: true
Whether redirect responses should be followed automatically.
Optionally, pass a function to dynamically decide based on the response object. The function is only called for 3xx responses, and an unchanged function is evaluated only once per response.
**Note:**
- If a
303is sent by the server in response to any request type (POST, DELETE, etc.), Got will request the resource pointed to in the location header via GET.
This is in accordance with the specification. You can optionally turn on this behavior also for other redirect codes - seemethodRewriting.- On cross-origin redirects, Got strips
host,cookie,cookie2,authorization, andproxy-authorization, as well as any credentials embedded in the URL.307and308redirects preserve the request method and replayable body, even across origins, as required by the specification.QUERYrequests also preserve replayable bodies on301and302redirects becauseQUERYis safe and idempotent. Same-origin301and302redirects that keep the request method also replay the body. Got fails instead of following when the body cannot be replayed.- Other cross-origin redirects can drop the request body and request body headers to avoid forwarding payloads to another origin.
- When a redirect rewrites the request to
GET, Got also strips request body headers.- Use
hooks.beforeRedirectfor app-specific sensitive headers.
import got from 'got';
const instance = got.extend({followRedirect: false});
const response = await instance('http://google.com');
console.log(response.headers.location);
//=> 'https://google.com'`maxRedirects`
Type: number
Default: 10
If exceeded, the request will be aborted and a MaxRedirectsError will be thrown.
import got from 'got';
const instance = got.extend({maxRedirects: 3});
try {
await instance('https://nghttp2.org/httpbin/absolute-redirect/5');
} catch (error) {
//=> 'Redirected 3 times. Aborting.'
console.log(error.message);
}`decompress`
Type: boolean
Default: true
Decompress the response automatically. This will set the accept-encoding header to gzip, deflate, br (and zstd on Node.js >= 22.15.0).
If disabled, a compressed response is returned as a Uint8Array. This may be useful if you want to handle decompression yourself.
[!NOTE]
Zstandard (zstd) compression support is available on Node.js >= 22.15.0 and will be automatically enabled when available.
import got from 'got';
const response = await got('https://google.com');
console.log(response.headers['content-encoding']);
//=> 'gzip'`strictContentLength`
Type: boolean
Default: true
Throw an error if the server response's content-length header value doesn't match the number of bytes received.
This is useful for detecting truncated responses and follows RFC 9112 requirements for message completeness.
[!NOTE]
- Responses without a
content-lengthheader are not validated.- When enabled and validation fails, a
ReadErrorwith codeERR_HTTP_CONTENT_LENGTH_MISMATCHwill be thrown.- Keep custom native responses in byte mode for exact validation. Calling
setEncoding()on the native response can lose the original byte count when decoding malformed input.
`dnsLookup`
Type: Function
Default: dns.lookup
Custom DNS resolution logic.
The function signature is the same as dns.lookup.
`dnsCache`
Type: {lookup: Function; clear?: Function} | boolean
A DNS cache instance used for making DNS lookups.
Useful when making lots of requests to different public hostnames.
Set to true to use Got's shared DNS cache.
When using got.extend(), set to false to opt out of a DNS cache configured by the parent instance.
Note:
- This should stay disabled when making requests to internal hostnames such as localhost, database.local etc.
- Got's built-in DNS cache uses
dns.resolve4(…)anddns.resolve6(…)under the hood and falls back todns.lookup(…)when no DNS records are found, which may lead to additional delay.- Because Got's built-in DNS cache resolves A and AAAA records separately, it cannot preserve OS-specific
verbatimaddress ordering fromdns.lookup(…).- If present,
clear(hostname?)can be called by user code to clear cached entries.
`dnsLookupIpVersion`
Type: 4 | 6
Default: undefined
The IP version to use. Specifying undefined will use the default configuration.
`request`
Type: Function<[ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) | [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) | undefined> | AsyncFunction<[ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) | [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) | undefined>
Default: Got's built-in HTTP/1.1 or HTTP/2 request implementation
Custom request function.
Returning undefined (or resolving to undefined) will fall back to Got's native request implementation.
`cache`
Type: object | false
Default: undefined
Cache adapter instance for storing cached response data.
`cacheOptions`
Type: object
Default: {}
Cache options used for the specified request.
`http2`
Type: boolean
Default: false
If true, Got will use its built-in HTTP/2 client when ALPN selects HTTP/2.
Note:
- ALPN negotiation will take place in order to determine if the server actually supports HTTP/2. If it doesn't, HTTP/1.1 will be used. When a custom
agent.httpsinstance is set, Got uses that native HTTPS agent directly and skips HTTP/2 negotiation.
Note:
- If the
requestoption returns a request or response, it controls the transport and Got's HTTP/2 client is bypassed. Returnundefinedto fall back to Got's built-in transport.
Note:
- There is no direct
h2cUpgrade support. However, you can provide ah2sessionoption in abeforeRequesthook. See an example.
import got from 'got';
const {statusCode} = await got(
'https://httpbin.org/anything',
{
http2: true
}
);
console.log(statusCode);
//=> 200`agent`
Type: object
Default: {}
An object with http, https and http2 properties.
Got will automatically resolve the protocol and use the corresponding agent. HTTP/2 uses Got's internal session pool by default. Set agent.http2 to false to disable HTTP/2 session pooling for the request. agent.http2 is only a pooling opt-out flag; custom HTTP/2 agents are not part of the public API. When http2 is enabled, a custom agent.https instance makes Got use the native HTTP/1.1 request path because Got's built-in HTTP/2 session pool does not support custom HTTPS agents.
{
http: http.globalAgent,
https: https.globalAgent
}`throwHttpErrors`
Type: boolean
Default: true
If true, it will throw when the status code is not 2xx / 3xx.
If this is disabled, requests that encounter an error status code will be resolved with the response instead of throwing. This may be useful if you are checking for resource availability and are expecting error responses.
`username`
Type: string
Default: ''
The username used for Basic authentication.
`password`
Type: string
Default: ''
The password used for Basic authentication.
`localAddress`
Type: string | undefined
Default: undefined
The local IP address used to make the request.
`createConnection`
Type: Function | undefined
Default: undefined
The function used to retrieve a net.Socket instance when the agent option is not used.
For HTTP/1, either return the socket synchronously or call callback(null, socket) asynchronously. Call callback(error) if socket creation fails.
When http2 is enabled for HTTPS, this function must synchronously return a tls.TLSSocket. Callback-based creation is not supported by HTTP/2 negotiation.
`https`
Type: object
See Advanced HTTPS API.
`pagination`
Type: object
See Pagination API.
`setHost`
Type: boolean
Default: true
Specifies whether or not to automatically add the Host header.
`maxHeaderSize`
Type: number | undefined
Default: undefined
Optionally overrides the value of --max-http-header-size (default 16KB: 16384).
`methodRewriting`
Type: boolean
Default: false
Specifies if the HTTP request method should be rewritten as GET on redirects.
As the specification prefers to rewrite the HTTP method only on 303 responses, this is Got's default behavior. Cross-origin 301 and 302 redirects also rewrite POST requests to GET by default to avoid forwarding request bodies to another origin. Setting methodRewriting to true will also rewrite same-origin 301 and 302 responses, as allowed by the spec. This is the behavior followed by curl and browsers.
Note:
- Got never performs method rewriting on
307and308responses, as this is explicitly prohibited by the specification.
`enableUnixSockets`
Type: boolean
Default: false
When enabled, requests can also be sent via UNIX Domain Sockets.
Warning
Make sure you do your own URL sanitizing if you accept untrusted user input for the URL.
Use the following URL scheme: PROTOCOL://unix:SOCKET:PATH
PROTOCOL-httporhttpsSOCKET- Absolute path to a UNIX domain socket, for example:/var/run/docker.sockPATH- Request path, for example:/v2/keys
import got from 'got';
await got('http://unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true});
// Or without protocol (HTTP by default)
await got('unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true});
// Enable Unix sockets for the whole instance.
const gotWithUnixSockets = got.extend({enableUnixSockets: true});
await gotWithUnixSockets('http://unix:/var/run/docker.sock:/containers/json');Methods
`options.merge(other: Options | OptionsInit)`
Merges other into the current instance.
If you look at the source code, you will notice that options track internal merge state.
Setters work a bit differently while merge is in progress.
`options.toJSON()`
Returns a new plain object that can be stored as JSON.
`options.createNativeRequestOptions()`
Creates a new object for native Node.js HTTP request options.
In other words, this translates Got options into Node.js options.
Note:
- Some other stuff, such as timeouts, is handled internally by Got.
`options.getRequestFunction()`
Returns a http.request-like function used to make the request.
`options.freeze()`
Makes the entire Options instance read-only.
Documentation 3 Streams (`documentation/3-streams.md`)
Stream API
Source code: source/core/index.ts
`got.stream(url, options, defaults)`
The function above is exposed by the got main interface and returns a new instance of Request.
`new Request(url, options, defaults)`
Extends: Duplex stream
This constructor takes the same arguments as the Got promise.
Note:
When piping to
ServerResponse, the headers will be automatically copied, except for hop-by-hop headers likeconnection,keep-alive,transfer-encoding, and any headers listed inconnection.
Whendecompressistrue(default) and the response is compressed, thecontent-encodingandcontent-lengthheaders are not copied, as the response is decompressed.
To filter which headers are copied, listen to theresponseevent and modifyresponse.headersbefore piping to the destination.
Note:
If the
body,jsonorformoption is used, this stream will be read-only. Checkstream.isReadonlyto detect this condition.
Note:
- While
got.post('https://example.com')resolves,got.stream.post('https://example.com')will hang indefinitely until a body is provided.- If there's no body on purpose, remember to
stream.end()or set the body option to an empty string.got.streamdoes not auto-end forPOST,PUT,OPTIONS,PATCH,DELETE, orQUERY. Callstream.end()when you are not piping a body.
import stream from 'node:stream';
import {pipeline as streamPipeline} from 'node:stream/promises';
import fs from 'node:fs';
import got from 'got';
// This example streams the GET response of a URL to a file.
await streamPipeline(
got.stream('https://sindresorhus.com'),
fs.createWriteStream('index.html')
);
// For payload-capable shortcut methods such as POST, PUT, PATCH, DELETE, and QUERY, `got.stream` returns a `stream.Writable`.
// This example POSTs the contents of a file to a URL.
await streamPipeline(
fs.createReadStream('index.html'),
got.stream.post('https://sindresorhus.com'),
new stream.PassThrough()
);
// In order to send one of these methods without a request body, explicitly specify an empty body:
await streamPipeline(
got.stream.post('https://sindresorhus.com', { body: '' }),
new stream.PassThrough()
)Please note that new stream.PassThrough() is required in order to catch read errors.
If it was missing then pipeline wouldn't catch any read errors because there would be no stream to pipe to.
In other words, it would only check errors when writing.
Tip:
- Avoid
from.pipe(to)as it doesn't forward errors.
`stream.options`
Type: Options
The options used to make the request.
`stream.response`
Type: IncomingMessage
The underlying IncomingMessage instance.
`stream.requestUrl`
Type: URL
The current URL object in this try.
`stream.redirectUrls`
Type: URL[]
An array of URLs of consecutive requests.
`stream.retryCount`
Type: number
The current retry count.
Note:
- Must be overriden when retrying.
`stream.ip`
Type: string | undefined
The destination IP address.
`stream.isAborted`
Type: boolean
Whether the request has been aborted or not.
`stream.socket`
Type: net.Socket | tls.Socket | undefined
The socket used for this particular request.
`stream.downloadProgress`
Type: Progress
An object representing how much data have been downloaded.
`stream.uploadProgress`
Type: Progress
An object representing how much data have been uploaded.
Granular upload progress is automatically supported for non-stream body (string/Uint8Array/TypedArray), json, and form options. For Node.js streams, you can use the chunk-data package to get granular progress:
import fs from 'node:fs';
import got from 'got';
import {chunkFromAsync} from 'chunk-data';
const filePath = 'large-file.bin';
const stream = fs.createReadStream(filePath);
const size = fs.statSync(filePath).size;
await got.post('https://httpbin.org/anything', {
body: chunkFromAsync(stream, 65_536),
headers: {
'content-length': size
}
})
.on('uploadProgress', progress => {
console.log(progress);
});`stream.timings`
Type: Timings
An object representing performance information.
Got computes the timings itself, with the same phases http-timer reports.
`stream.isFromCache`
Type: boolean | undefined
Whether the response has been fetched from cache.
`stream.reusedSocket`
Type: boolean
Whether the socket was used for other previous requests.
`stream.isReadonly`
Type: boolean
Whether the stream is read-only. Returns true when body, json, or form options are provided.
Events
`stream.on('response', …)`
`response`
Type: PlainResponse
This is emitted when a HTTP response is received.
import {pipeline as streamPipeline} from 'node:stream/promises';
import {createWriteStream} from 'node:fs';
import got from 'got';
const readStream = got.stream('http://example.com/image.png', {throwHttpErrors: false});
const onError = error => {
// Do something with it.
};
readStream.on('response', async response => {
if (response.headers.age > 3600) {
console.log('Failure - response too old');
readStream.destroy(); // Destroy the stream to prevent hanging resources.
return;
}
// Prevent `onError` being called twice.
readStream.off('error', onError);
try {
await streamPipeline(
readStream,
createWriteStream('image.png')
);
console.log('Success');
} catch (error) {
onError(error);
}
});
readStream.once('error', onError);Example: Reading HTTP error response bodies
By default, Got throws HTTP errors before the stream becomes readable. To read error response bodies:
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';
const stream = got.stream('https://httpbin.org/status/404', {
throwHttpErrors: false
});
stream.on('response', response => {
if (!response.ok) {
console.log(`HTTP Error: ${response.statusCode}`);
// Stream is readable, you can pipe or read the error body
}
});
await streamPipeline(stream, process.stdout);Example: Filter headers when proxying to ServerResponse
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';
import express from 'express';
const app = express();
// Allowlist specific headers when proxying
app.get('/proxy', async (request, response) => {
await streamPipeline(
got.stream(request.query.url).on('response', upstreamResponse => {
// Only allow specific headers
for (const header of Object.keys(upstreamResponse.headers)) {
if (!['content-type', 'content-length'].includes(header.toLowerCase())) {
delete upstreamResponse.headers[header];
}
}
}),
response
);
});`stream.on('downloadProgress', …)`
`progress`
Type: Progress
This is emitted on every time stream.downloadProgress is updated.
`stream.on('uploadProgress', …)`
`progress`
Type: Progress
This is emitted on every time stream.uploadProgress is updated.
`stream.on('retry', …)`
To enable retrying when using streams, a retry handler must be attached.
When this event is emitted, you should reset the stream you were writing to and prepare the body again.
Note:
HTTPErrors cannot be retried ifoptions.throwHttpErrorsisfalse.
This is because stream data is saved toerror.response.bodyand streams can be read only once.- For the Promise API, there is no such limitation.
- If you need to read HTTP error response bodies without retry, see Reading HTTP error response bodies.
`retryCount`
Type: number
The current retry count.
`error`
Type: RequestError
The error that caused this retry.
`createRetryStream`
Type: (options?: OptionsInit) => Request
import fs from 'node:fs';
import got from 'got';
let writeStream;
const fn = retryStream => {
const options = {
headers: {
foo: 'bar'
},
};
const stream = retryStream ?? got.stream('https://example.com', options);
if (writeStream) {
writeStream.destroy();
}
writeStream = fs.createWriteStream('example-com.html');
stream.pipe(writeStream);
// If you don't attach the listener, it will NOT make a retry.
// It automatically checks the listener count so it knows whether to retry or not :)
stream.once('retry', (retryCount, error, createRetryStream) => {
fn(createRetryStream()); // or: fn(createRetryStream(optionsToMerge))
});
};
fn();`stream.on('redirect', …)`
`updatedOptions`
Type: Options
The new options used to make the next request.
`response`
Type: IncomingMessage
The IncomingMessage instance the redirect came from.
Internal usage
These are the functions used internally by Got.
Other non-documented functions are private and should not be accessible.
`stream.flush()`
This function is executed automatically by Got. It marks the current stream as ready. If an error occurs before stream.flush() is called, it's thrown immediately after stream.flush().
`stream._beforeError(error)`
This function is called instead of stream.destroy(error), required in order to execute async logic, such as reading the response (e.g. when ERR_NON_2XX_3XX_RESPONSE occurs).
`stream._noPipe`
Type: boolean
Whether piping is disabled or not. This property is used by the Promise API.
`Response`
Source code: source/core/response.ts
Extends: IncomingMessage
`requestUrl`
Type: URL
The original request URL. It is the first argument when calling got(…).
`redirectUrls`
Type: URL[]
The redirect URLs.
`request`
Type: Request
The underlying Got stream.
`ip`
Type: string
The server's IP address.
Note:
- Not available when the response is cached.
`isFromCache`
Type: boolean
Whether the response comes from cache or not.
`ok`
Type: boolean
Whether the response was successful
Note:
- A request is successful when the status code of the final request is
2xxor3xx.- When following redirects, a request is successful only when the status code of the final request is
2xx.304responses are always considered successful.- Got throws automatically when
response.okisfalseandthrowHttpErrorsistrue.- To read HTTP error response bodies with streams, set
throwHttpErrors: falseand checkresponse.okin theresponseevent handler. See example above.
`statusCode`
Type: number
The HTTP status code.
`url`
Type: string
The final URL after all redirects.
`timings`
Type: Timings | undefined
The object contains the following properties:
start- Time when the request started.socket- Time when a socket was assigned to the request.lookup- Time when the DNS lookup finished.connect- Time when the socket successfully connected.secureConnect- Time when the socket securely connected.upload- Time when the request finished uploading.response- Time when the request firedresponseevent.end- Time when the response firedendevent.error- Time when the request firederrorevent.abort- Time when the request firedabortevent.phaseswait-timings.socket - timings.startdns-timings.lookup - timings.sockettcp-timings.connect - timings.lookuptls-timings.secureConnect - timings.connectrequest-timings.upload - (timings.secureConnect || timings.connect)firstByte-timings.response - timings.uploaddownload-timings.end - timings.responsetotal-(timings.end || timings.error || timings.abort) - timings.start
If something has not been measured yet, it will be undefined.
The entire property is undefined for cached responses and responses returned directly by hooks or custom request functions without timing information.
Note: The time is a number representing the milliseconds elapsed since the UNIX epoch.
`retryCount`
Type: number
The same as request.retryCount.
`rawBody`
Type: Uint8Array
Note:
- This property is only accessible when using Promise API.
The raw response body bytes.
`body`
Type: unknown
Note:
- This property is only accessible when using Promise API.
The parsed response body.
`aborted`
Type: boolean
Node's IncomingMessage.aborted: whether the response was aborted before it completed.
[!NOTE]
Over HTTP/2, a stream reset with an error code before the response ended fails the request, either as an aborted response or as a stream error. A reset withNO_ERRORis harder: it is how a server stops an upload it does not need, and it can come with or without the end of the response. Got accepts such a response when it saw the end of the response first, or when thecontent-lengthheader proves it complete. While the client is still uploading, it reports any other response as aborted. Detecting this is best effort for a response without acontent-lengthheader: when the end of the response and the reset arrive together, a complete response can be reported as aborted. ANO_ERRORreset after the upload has finished, such as for aGET, cannot be told apart from the end of the response, so a body without acontent-lengthheader cut off that way is delivered as complete.
`complete`
Type: boolean
If true, the response has been fully parsed.
`socket`
Type: net.Socket | tls.TLSSocket
The same as request.socket.
`headers`
Type: object<string, string>
The response headers.
`statusMessage`
Type: string
The status message corresponding to the status code.
Documentation 4 Pagination (`documentation/4-pagination.md`)
Pagination API
Source code: source/create.ts
How does the `Link` header work?
The RFC5988 defines how the Link header looks like.
When the response has been processed, Got looks for the reference of the next relation.
This way Got knows the URL it should visit afterwards. The header can look like this:
Link: <https://api.github.com/repositories/18193978/commits?page=2>; rel="next", <https://api.github.com/repositories/18193978/commits?page=44>; rel="last"By default, Got looks only at the next relation. To use other relations, you need to customize the paginate function below.
`got.paginate(url, options?)`
`got.paginate.each(url, options?)`
Returns an async iterator.
This is memory efficient, as the logic is executed immediately when new data comes in.
import got from 'got';
const countLimit = 10;
const pagination = got.paginate(
'https://api.github.com/repos/sindresorhus/got/commits',
{
pagination: {countLimit}
}
);
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);
for await (const commitData of pagination) {
console.log(commitData.commit.message);
}`got.paginate.all(url, options?)`
Note:
- Querying a large dataset significantly increases memory usage.
Returns a Promise for an array of all results.
import got from 'got';
const countLimit = 10;
const results = await got.paginate.all('https://api.github.com/repos/sindresorhus/got/commits', {
pagination: {countLimit}
});
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);
console.log(results);`parseLinkHeader(link)`
Type: (link: string) => Array<{reference: string; parameters: Record<string, string>}>
Parses a Link header into one entry per link. It is exported from Got so the default pagination.paginate can be copied or replaced.
[!NOTE]
- Link targets keep their original case, but parameter names are lowercased, so read
parameters.relrather thanparameters.REL.- The first occurrence of a repeated parameter wins, and a parameter may be present without a value, in which case its value is an empty string.
- A link without any parameters parses to an empty
parametersobject instead of throwing.- Values keep their surrounding quotes, so
rel="next"yields the value"next"including the quotes.parametershas no prototype, soparameters.toStringisundefinedand inherited names such asconstructorare only present when the header sets them.
import {parseLinkHeader} from 'got';
console.log(parseLinkHeader('<https://example.com/2>; rel="next"'));
//=> [{reference: 'https://example.com/2', parameters: {rel: '"next"'}}]`pagination`
Type: object
Default:
{
transform: (response: Response) => {
if (response.request.options.responseType === 'json') {
return response.body;
}
const body = response.request.options.responseType === 'buffer'
? decodeUint8Array(response.body as Uint8Array, response.request.options.encoding)
: response.body as string;
return response.request.options.parseJson(body);
},
paginate: ({response}) => {
const rawLinkHeader = response.headers.link;
if (typeof rawLinkHeader !== 'string' || rawLinkHeader.trim() === '') {
return false;
}
const parsed = parseLinkHeader(rawLinkHeader);
const next = parsed.find(entry => !Object.hasOwn(entry.parameters, 'anchor') && entry.parameters.rel?.replaceAll('"', '').toLowerCase().split(/\s+/v).includes('next'));
if (next) {
return {
url: next.reference
};
}
return false;
},
filter: () => true,
shouldContinue: () => true,
countLimit: Number.POSITIVE_INFINITY,
backoff: 0,
requestLimit: 10_000,
stackAllItems: false
}This option represents the pagination object.
The default paginate uses the exported parseLinkHeader(…) helper.
`transform`
Type: Function
Default: Parses text and buffer responses as JSON using the configured encoding, and returns JSON responses unchanged.
A function that transforms Response into an array of items.
This is where you should do the parsing.
`paginate`
Type: Function
Default: Link header logic
By default, links with an anchor parameter are ignored, as permitted by RFC 8288, section 3.2.
The function takes an object with the following properties:
response- The current response object,currentItems- Items from the current response,allItems- An empty array, unlessstackAllItemsistrue, otherwise it contains all emitted items.
It should return an object representing Got options pointing to the next page. If there is no next page, false should be returned instead. The function may also be async and return a promise for either value.
The options are merged automatically with the previous request.
Therefore the options returned by pagination.paginate(…) must reflect changes only.
Note:
- The
urloption (if set) accepts a string orURLinstance. Relative strings resolve against the current request URL. After a cross-origin redirect, that means the final redirected URL, not the originalprefixUrl.- When pagination navigates to a different origin, Got strips inherited sensitive headers such as
authorization,cookie, andproxy-authorization. If you trust the next-page URL and want to forward a sensitive header, return it explicitly frompagination.paginate(…).
`filter`
Type: Function
Default: ({item, currentItems, allItems}) => true
Whether the item should be emitted or not.
`shouldContinue`
Type: Function
Default: ({item, currentItems, allItems}) => true
Note:
- This function executes only when
filterreturnstrue.
For example, if you need to stop before emitting an entry with some flag, you should use ({item}) => !item.flag.
If you want to stop after emitting the entry, you should use ({item, allItems}) => allItems.some(item => item.flag) instead.
`countLimit`
Type: number
Default: Number.POSITIVE_INFINITY
The maximum amount of items that should be emitted.
`backoff`
Type: number
Default: 0
Milliseconds to wait before the next request is triggered.
`requestLimit`
Type: number
Default: 10000
The maximum amount of request that should be triggered.
Note:
- Retries on failure are not counted towards this limit.
`stackAllItems`
Type: boolean
Default: false
Defines how allItems is managed in pagination.paginate, pagination.filter and pagination.shouldContinue.
By default, allItems is always an empty array. Setting this to true will significantly increase memory usage when working with a large dataset.
Example
In this example we will use searchParams instead of Link header.
Just to show how you can customize the paginate function.
The reason filter looks exactly the same like shouldContinue is that the latter will tell Got to stop once we reach our timestamp.
The filter function is needed as well, because in the same response we can get results with different timestamps.
/* Detailed source-code truncated for AI context efficiency. */Documentation 5 Https (`documentation/5-https.md`)
Advanced HTTPS API
`https`
Type: object
This option represents the options used to make HTTPS requests.
`alpnProtocols`
Type: string[]
Default: undefined
Acceptable ALPN protocols.
By default no ALPN protocols are offered. When the http2 option is true, the HTTP/2 negotiation path offers ['h2', 'http/1.1'] unless this option is set.
`rejectUnauthorized`
Type: boolean
Default: true
If true, it will throw on invalid certificates, such as expired or self-signed ones.
`checkServerIdentity`
Type: (hostname: string, certificate: DetailedPeerCertificate) => Error | undefined
Default: tls.checkServerIdentity
Custom check of the certificate. Useful for pinning certificates.
The function must return undefined if the check succeeded.
If it failed, an Error should be returned.
Note:
- In order to have the function called, the certificate must not be expired, self-signed nor with an untrusted-root.
Check Node.js docs for an example.
`serverName`
Type: string
Default: undefined
Server name for the Server Name Indication (SNI) TLS extension.
This is useful when requesting to servers that don't have a proper domain name but use a certificate with a known CN/SAN.
import got from 'got';
// Request to IP address with specific servername for TLS
await got('https://192.168.1.100', {
https: {
serverName: 'example.com'
}
});`certificateAuthority`
Type: string | Uint8Array | string[] | Uint8Array[]
Note:
- The option has been renamed from the
caTLS option for better readability.
Overrides trusted CA certificates.
Defaults to CAs provided by Mozilla.
import got from 'got';
// Single Certificate Authority
await got('https://example.com', {
https: {
certificateAuthority: fs.readFileSync('./my_ca.pem')
}
});`key`
Type: string | Uint8Array | string[] | Uint8Array[] | object[]
Private keys in PEM format.
Multiple keys with different passphrases can be provided as an array of {pem: <string | Uint8Array>, passphrase: <string>}.
Note:
- Encrypted keys will be decrypted with
https.passphrase.
`passphrase`
Type: string
Shared passphrase used for a single private key and/or a PFX.
`certificate`
Type: string | Uint8Array | string[] | Uint8Array[]
Note:
- The option has been renamed from the
certTLS option for better readability.
Certificate chains in PEM format.
One certificate chain should be provided per private key.
When providing multiple certificate chains, they do not have to be in the same order as their private keys in https.key.
`pfx`
Type: string | Uint8Array | Array<string | Uint8Array | object>
PFX or PKCS12 encoded private key and certificate chain. Using https.pfx is an alternative to providing https.key and https.certificate individually. A PFX is usually encrypted, then https.passphrase will be used to decrypt it.
Multiple PFX can be provided as an array containing strings, byte arrays, and objects like:
{
buffer: string | Uint8Array,
passphrase?: string
}`certificateRevocationLists`
Type: string | Uint8Array | string[] | Uint8Array[]
Note:
- The option has been renamed from the
crlTLS option for better readability.
`secureOptions`
Type: number
Optionally affect the OpenSSL protocol behavior, which is not usually necessary. This should be used carefully if at all!
The value is a numeric bitmask of the SSL_OP_* options from OpenSSL.
For example, to allow connections to legacy servers that do not support secure renegotiation, you can use crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT.
import crypto from 'node:crypto';
import got from 'got';
// Allow connections to servers with legacy renegotiation
await got('https://legacy-server.com', {
https: {
secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT
}
});Other HTTPS options
Documentation for the below options.
ciphersdhparamsignatureAlgorithms(renamed fromsigalgs)minVersionmaxVersionhonorCipherOrdertlsSessionLifetime(renamed fromsessionTimeout)ecdhCurve
Examples
/* Detailed source-code truncated for AI context efficiency. */Documentation 6 Timeout (`documentation/6-timeout.md`)
Timeout options
Source code: source/core/timed-out.ts
It is a good practice to set a timeout to prevent hanging requests.
By default, there is no timeout set.
Note: The retry configuration also applies to timeouts.
All numbers refer to milliseconds.
import got from 'got';
const {timings} = await got('https://example.com', {
timeout: {
lookup: 100,
connect: 50,
secureConnect: 50,
socket: 1000,
send: 10000,
response: 1000
}
});
// Alternatively:
const {timings} = await got('https://example.com', {
timeout: {
request: 10000
}
});
console.log(timings);
// {
// start: 1625474926602,
// socket: 1625474926605,
// lookup: 1625474926610,
// connect: 1625474926617,
// secureConnect: 1625474926631,
// upload: 1625474926631,
// response: 1625474926638,
// end: 1625474926642,
// error: undefined,
// abort: undefined,
// phases: {
// wait: 3,
// dns: 5,
// tcp: 7,
// tls: 14,
// request: 0,
// firstByte: 7,
// download: 4,
// total: 40
// }
// }`timeout`
Type: object
This object describes the maximum allowed time for particular events.
`lookup`
Type: number
Starts when a socket is assigned.
Ends when the hostname has been resolved.
Does not apply when using a Unix domain socket.
Does not apply when passing an IP address.
It is preferred to not use any greater value than 100.
`connect`
Type: number
Starts when lookup completes.
Ends when the socket is fully connected.
If lookup does not apply to the request, this event starts when the socket is assigned and ends when the socket is connected.
`secureConnect`
Type: number
Starts when connect completes.
Ends when the handshake process completes.
This timeout applies only to HTTPS requests.
`socket`
Type: number
Starts when the socket is connected.
Resets when new data is transferred.
It is the same as request.setTimeout(timeout) which calls socket.setTimeout(timeout) after a socket is assigned to this request and is connected.
`send`
Type: number
Starts when the socket is connected.
Ends when all data have been written to the socket.
Note:
- This does not assure the data have been received by the other end!
- It only assures that the data have been passed to the underlying OS.
`response`
Type: number
Starts when request has been flushed.
Ends when the headers are received.
`read`
Type: number
Starts when the headers are received.
Ends when the response's end event fires.
`request`
Type: number
Starts when the request is initiated, after the beforeRequest hooks of the first request have run.
Ends when the final response's end event fires.
In other words, this is the global timeout. Redirects share the same budget, including the time spent in beforeRedirect hooks, cookie lookups, and custom request functions between them.
Hooks may change timeout.request while the request is in flight. A value set in a beforeRedirect hook applies once that hook has finished, and a redirected beforeRequest hook can raise or disable it immediately.
Documentation 7 Retry (`documentation/7-retry.md`)
Retry API
Note:
If you're looking for retry implementation using streams, check out the Retry Stream API.
Tip:
You can trigger a retry by throwing the
RetryErrorin any hook.
Tip:
The
afterResponsehook exposes a dedicated function to retry with merged options. Read more.
`retry`
Type: object
Default:
{
limit: 2,
methods: [
'GET',
'PUT',
'HEAD',
'DELETE',
'OPTIONS',
'TRACE',
'QUERY'
],
statusCodes: [
408,
413,
429,
500,
502,
503,
504,
521,
522,
524
],
errorCodes: [
'ETIMEDOUT',
'ECONNRESET',
'EADDRINUSE',
'ECONNREFUSED',
'EPIPE',
'ENOTFOUND',
'ENETUNREACH',
'EAI_AGAIN'
],
maxRetryAfter: undefined,
calculateDelay: ({computedValue}) => computedValue,
backoffLimit: Number.POSITIVE_INFINITY,
noise: 100,
enforceRetryRules: true
}This option represents the retry object.
enforceRetryRules decides whether the limits above are applied before calculateDelay runs. See calculateDelay.
Undefined settings preserve inherited values, except maxRetryAfter, where undefined restores the request timeout fallback.
`limit`
Type: number
The maximum retry count.
`methods`
Type: string[]
The allowed methods to retry on.
Note:
- By default, Got does not retry on
POST.
`statusCodes`
Type: number[]
Note:
- Only unsuccessful requests are retried. In order to retry successful requests, use an
afterResponsehook.
The allowed HTTP status codes to retry on.
`errorCodes`
Type: string[]
The allowed error codes to retry on.
ETIMEDOUT- One of the timeout limits was reached.ECONNRESET- The connection was forcibly closed.EADDRINUSE- Could not bind to any free port.ECONNREFUSED- The connection was refused by the server.EPIPE- The remote side of the stream being written has been closed.ENOTFOUND- Could not resolve the hostname to an IP address.ENETUNREACH- No internet connection.EAI_AGAIN- DNS lookup timed out.
`maxRetryAfter`
Type: number | undefined
Default: options.timeout.request
The upper limit of retry-after header. If undefined, it will use options.timeout as the value.
If the limit is exceeded, the request is aborted.
`calculateDelay`
Type: Function
(retryObject: RetryObject) => Promisable<number>interface RetryObject {
attemptCount: number;
retryOptions: RetryOptions;
error: RequestError;
computedValue: number;
retryAfter?: number;
}The function used to calculate the delay before the next request is made. Returning 0 aborts the retry.
Delays above 2_147_483_647 milliseconds (Node.js's timer limit), including Infinity, abort the retry instead of triggering an immediate retry. This also applies to delays computed from Retry-After.
Note:
- By default, retry rules are enforced before
calculateDelayruns (enforceRetryRules: true), so this function is only called when a retry is allowed.- If you set
enforceRetryRules: false,calculateDelaytakes full control of retry behavior. In that mode, checkcomputedValueand return0when it is0to preserve default retry safeguards.
Tip:
- This is especially useful when you want to scale down the computed value.
import got from 'got';
await got('https://httpbin.org/anything', {
retry: {
limit: 3,
calculateDelay: ({computedValue}) => {
// When computedValue is 0, the default logic says don't retry
// (limit exceeded, non-retryable error, etc.)
if (computedValue === 0) {
return 0;
}
// Scale down the delay
return computedValue / 10;
}
}
});`backoffLimit`
Type: number
The upper limit of the exponential backoff. The noise is added after this limit is applied.
By default, the computedValue is calculated in the following way:
Math.max(1, Math.min((2 ** (attemptCount - 1)) * 1000, backoffLimit) + noise)Computed delays are at least 1 millisecond. Set both backoffLimit and noise to 0 for immediate retries. Returning 0 from calculateDelay still aborts the retry.
The delay increases exponentially.
In order to prevent this, you can set this value to a fixed value, such as 1000.
`noise`
Type: number
The maximum acceptable retry noise in the range of -100 to +100.
Documentation 8 Errors (`documentation/8-errors.md`)
Errors
Source code:
All Got errors contain various metadata, such as:
code- A string likeERR_NON_2XX_3XX_RESPONSE,options- An instance ofOptions,request- An instance of Got Stream,response(optional) - An instance of Got Response,timings(optional) - Points toresponse.timings.
Capturing async stack traces
Read the article here.
[!NOTE]
- The error codes may differ when the root error has a
codeproperty set.- The root error will be propagated as is via the
causeproperty.
`RequestError`
Code: ERR_GOT_REQUEST_ERROR
When a request fails. Contains a code property with error class code, like ECONNREFUSED. All the errors below inherit this one.
`CacheError`
Code: ERR_CACHE_ACCESS
When a cache method fails, for example, if the database goes down or there's a filesystem error.
`ReadError`
Code: ERR_READING_RESPONSE_STREAM or ERR_HTTP_CONTENT_LENGTH_MISMATCH
When reading from response stream fails.
The error code will be ERR_HTTP_CONTENT_LENGTH_MISMATCH when the strictContentLength option is enabled and the server specifies a content-length header but the actual number of bytes received doesn't match.
`ParseError`
Code: ERR_BODY_PARSE_FAILURE
When server response code is 2xx, and parsing body fails. Includes a response property.
`UploadError`
Code: ERR_UPLOAD
When the request body is a stream and an error occurs while reading from that stream.
`HTTPError`
Code: ERR_NON_2XX_3XX_RESPONSE
When the request is unsuccessful.
A request is successful when the status code of the final request is 2xx or 3xx.
When following redirects, a request is successful only when the status code of the final request is 2xx.
[!NOTE]
304responses are always considered successful.
`MaxRedirectsError`
Code: ERR_TOO_MANY_REDIRECTS
When the server redirects you more than ten times. Includes a response property.
`UnsupportedProtocolError`
[!NOTE]
This error is not public.
Code: ERR_UNSUPPORTED_PROTOCOL
When given an unsupported protocol.
`TimeoutError`
Code: ETIMEDOUT
When the request is aborted due to a timeout. Includes an event (a string) property along with timings.
`RetryError`
Code: ERR_RETRYING
Always triggers a new retry when thrown.
`AbortError`
Code: ERR_ABORTED
When the request is aborted with AbortController.abort().
Documentation 9 Hooks (`documentation/9-hooks.md`)
Hooks API
`hooks`
Type: object<string, Function[]>
This option represents the hooks to run. Thrown errors will be automatically converted to RequestError.
`init`
Type: InitHook[]
Default: []
(plainRequestOptions: OptionsInit, options: Options) => voidCalled with the plain request options, right before their normalization.
The second argument represents the current Options instance.
Note:
- This hook must be synchronous.
Note:
- This is called every time options are merged.
Note:
- The
optionsobject may not have theurlproperty. To modify it, use abeforeRequesthook instead.
Note:
- This hook is called when a new instance of
Optionsis created.- Do not confuse this with the creation of
Requestorgot(…).
Note:
- When using
got(url)orgot(url, undefined, defaults)this hook will not be called.
This is especially useful in conjunction with got.extend() when the input needs custom handling.
For example, this can be used to fix typos to migrate from older versions faster.
import got from 'got';
const instance = got.extend({
hooks: {
init: [
plain => {
if ('followRedirects' in plain) {
plain.followRedirect = plain.followRedirects;
delete plain.followRedirects;
}
}
]
}
});
// Normally, the following would throw:
const response = await instance(
'https://example.com',
{
followRedirects: true
}
);
// There is no option named `followRedirects`, but we correct it in an `init` hook.Or you can create your own option and store it in a context:
import got from 'got';
const instance = got.extend({
hooks: {
init: [
(plain, options) => {
if ('secret' in plain) {
options.context.secret = plain.secret;
delete plain.secret;
}
}
],
beforeRequest: [
options => {
options.headers.secret = options.context.secret;
}
]
}
});
const {headers} = await instance(
'https://httpbin.org/anything',
{
secret: 'passphrase'
}
).json();
console.log(headers.Secret);
//=> 'passphrase'`beforeRequest`
Type: BeforeRequestHook[]
Default: []
(options: NormalizedOptions, context: BeforeRequestHookContext) => Promisable<void | ClientRequest | IncomingMessage | ResponseLike>Called right before making the request with options.createNativeRequestOptions().
This hook is especially useful in conjunction with got.extend() when you want to sign your request.
The second parameter is a context object with the following properties:
retryCount- The current retry count (0 for the initial request, 1+ for retries).
Note:
- Got will make no further changes to the request before it is sent.
Note:
- Changing
options.jsonoroptions.formhas no effect on the request. You should changeoptions.bodyinstead. If needed, update theoptions.headersaccordingly.
import got from 'got';
const response = await got.post(
'https://httpbin.org/anything',
{
json: {payload: 'old'},
hooks: {
beforeRequest: [
(options, context) => {
options.body = JSON.stringify({payload: 'new'});
options.headers['content-length'] = new TextEncoder().encode(options.body).byteLength.toString();
}
]
}
}
);You can use context.retryCount to conditionally modify behavior based on whether it's the initial request or a retry:
import got from 'got';
const response = await got('https://httpbin.org/status/500', {
retry: {
limit: 2
},
hooks: {
beforeRequest: [
(options, context) => {
// Only log on initial request, not on retries
if (context.retryCount === 0) {
console.log('Making initial request');
}
}
]
}
});Tip:
- You can indirectly override the
requestfunction by early returning aClientRequest-like instance or aIncomingMessage-like instance. This is very useful when creating a custom cache mechanism.- Read more about this tip.
`beforeRedirect`
Type: BeforeRedirectHook[]
Default: []
(updatedOptions: Options, plainResponse: PlainResponse) => Promisable<void>The equivalent of beforeRequest but when redirecting.
Tip:
- This is especially useful when you want to avoid dead sites.
- Use this to remove app-specific sensitive headers on redirect. Got already strips
host,cookie,cookie2,authorization, andproxy-authorizationon cross-origin redirects, and strips request body headers when a redirect rewrites the request toGET.
import got from 'got';
const response = await got('https://example.com', {
hooks: {
beforeRedirect: [
(options, response) => {
if (options.url.hostname === 'dead-site.example') {
options.url = 'https://fallback-site.example';
}
}
]
}
});`beforeRetry`
Type: BeforeRetryHook[]
Default: []
(error: RequestError, retryCount: number) => Promisable<void>The equivalent of beforeError but when retrying. Additionally, there is a second argument retryCount, the current retry number.
Note:
- When using the Stream API, this hook is ignored.
Note:
- When retrying, the
beforeRequesthook is called afterwards.
Note:
- If no retry occurs, the
beforeErrorhook is called instead.
This hook is especially useful when you want to retrieve the cause of a retry.
import got from 'got';
await got('https://httpbin.org/status/500', {
hooks: {
beforeRetry: [
(error, retryCount) => {
console.log(`Retrying [${retryCount}]: ${error.code}`);
// Retrying [1]: ERR_NON_2XX_3XX_RESPONSE
}
]
}
});`beforeCache`
Type: BeforeCacheHook[]
Default: []
(response: Pick<PlainResponse, 'headers' | 'statusCode' | 'statusMessage'>) => false | voidCalled right before the response is cached. Allows you to control caching behavior by modifying response properties or preventing caching entirely.
This is especially useful when you want to prevent caching of specific responses or modify cache headers.
Return value:
false- Prevent caching (remaining hooks are skipped)void/undefined- Use default caching behavior (mutations take effect)
Modifying the response:
- Hooks can directly mutate response properties like
headers,statusCode, andstatusMessage- Mutations to
response.headersaffect how the caching layer decides whether to cache the response and for how long- Header changes are applied to what gets cached, not to the response the user receives. The
statusCodeandstatusMessageyou set are also what the caller receives, because the caching layer records the status after this hook returns
Note:
- This hook is only called when the
cacheoption is enabled.
Note:
- This hook must be synchronous. It cannot return a Promise. If you need async logic to determine caching behavior, use a
beforeRequesthook instead.
Note:
- When returning
false, remaining hooks are skipped. The response headers the user receives are NOT modified - only the caching layer sees modified headers.
Note:
- If a hook throws an error, it will be propagated and the request will fail. This is consistent with how other hooks in Got handle errors.
Note:
- At this stage, the response body has not been read yet - it's still a stream. Properties like
response.bodyandresponse.rawBodyare not available. You can only inspect/modify response headers and status code.
import got from 'got';
// Simple: Don't cache errors
const instance = got.extend({
cache: new Map(),
hooks: {
beforeCache: [
(response) => response.statusCode >= 400 ? false : undefined
]
}
});
await instance('https://example.com');import got from 'got';
// Advanced: Modify headers for fine control
const instance2 = got.extend({
cache: new Map(),
hooks: {
beforeCache: [
(response) => {
// Force caching with explicit duration
// Mutations work directly - no need to return
response.headers['cache-control'] = 'public, max-age=3600';
}
]
}
});`afterResponse`
Type: AfterResponseHook[]
Default: []
(response: Response, retryWithMergedOptions: (options: OptionsInit) => never) => Promisable<Response | RequestPromise<Response>>Each function should return the response. This is especially useful when you want to refresh an access token.
Note:
- When using the Stream API, this hook is ignored.
Note:
- Calling the
retryWithMergedOptionsfunction will triggerbeforeRetryhooks. By default, remainingafterResponsehooks are removed to prevent duplicate execution. To preserve remaining hooks on retry, setpreserveHooks: truein the options passed toretryWithMergedOptions. In case of an error,beforeRetryhooks will be called instead.- Meanwhile, the
init,beforeRequest, andbeforeRedirecthooks, as well as already executedafterResponsehooks, are skipped.
Note:
- To preserve remaining
afterResponsehooks after callingretryWithMergedOptions, setpreserveHooks: truein the options passed toretryWithMergedOptions. This is useful when you want hooks to run on retried requests.
Warning:
- Be cautious when using
preserveHooks: true. If a hook unconditionally callsretryWithMergedOptionswithpreserveHooks: true, it will create an infinite retry loop. Always ensure hooks have proper conditional logic to avoid infinite retries.
import got from 'got';
const instance = got.extend({
hooks: {
afterResponse: [
(response, retryWithMergedOptions) => {
// Unauthorized
if (response.statusCode === 401) {
// Refresh the access token
const updatedOptions = {
headers: {
token: getNewToken()
}
};
// Update the defaults
instance.defaults.options.merge(updatedOptions);
// Make a new retry
return retryWithMergedOptions(updatedOptions);
}
// No changes otherwise
return response;
}
],
beforeRetry: [
error => {
// This will be called on `retryWithMergedOptions(...)`
}
]
},
mutableDefaults: true
});Example with preserveHooks:
import got from 'got';
const instance = got.extend({
hooks: {
afterResponse: [
(response, retryWithMergedOptions) => {
if (response.statusCode === 401) {
return retryWithMergedOptions({
headers: {
authorization: getNewToken()
},
preserveHooks: true // Keep remaining hooks
});
}
return response;
},
(response) => {
// This hook will run on the retried request
// (the original request is interrupted when the first hook triggers a retry)
console.log('Response received:', response.statusCode);
return response;
}
]
}
});`beforeError`
Type: BeforeErrorHook[]
Default: []
(error: RequestError) => Promisable<Error>Called with a RequestError instance. The error is passed to the hook right before it's thrown.
This hook can return any Error instance, allowing you to:
- Return custom error classes for better error handling in your application
- Extend
RequestErrorwith additional properties - Return plain
Errorinstances when you don't need Got-specific error information
This is especially useful when you want to have more detailed errors or maintain backward compatibility with existing error handling code.
import got from 'got';
// Modify and return the error
await got('https://api.github.com/repos/sindresorhus/got/commits', {
responseType: 'json',
hooks: {
beforeError: [
error => {
const {response} = error;
if (response && response.body) {
error.name = 'GitHubError';
error.message = `${response.body.message} (${response.statusCode})`;
}
return error;
}
]
}
});
// Return a custom error class
class CustomAPIError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'CustomAPIError';
this.statusCode = statusCode;
}
}
await got('https://api.example.com/endpoint', {
hooks: {
beforeError: [
error => {
// Return a custom error for backward compatibility with your application
return new CustomAPIError(
error.message,
error.response?.statusCode
);
}
]
}
});Documentation Async Stack Traces (`documentation/async-stack-traces.md`)
Capturing async stack traces
Caution:
- Capturing async stack traces can severely degrade performance!
Want to skip the article? See the Conclusion where we discuss a mediocre solution.
We live in a world full of bugs. Software is getting more and more complicated, which makes debugging increasingly more difficult. Ever had an error with no idea where it came from? Yeah, it's often not easy to track this down.
You might have noticed that the .stack of an error sometimes look incomplete. This is often due to the execution of an async function that is triggered by a timer. Following the example:
await new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('here'));
});
});file:///home/szm/Desktop/got/demo.js:3
reject(new Error('here'));
^
Error: here
at Timeout._onTimeout (file:///home/szm/Desktop/got/demo.js:3:10)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)The stack trace does not show where the timeout was set. It's currently not possible to determine this with the native Promises. However, bluebird exposes an option dedicated to capturing async stack traces:
import Bluebird from 'bluebird';
Bluebird.config({longStackTraces: true});
global.Promise = Bluebird;
await new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('here'));
});
});node:internal/process/esm_loader:74
internalBinding('errors').triggerUncaughtException(
^
Error: here
at Timeout._onTimeout (file:///home/szm/Desktop/got/demo.js:7:10)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
From previous event:
at file:///home/szm/Desktop/got/demo.js:5:7
at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
at async Loader.import (node:internal/modules/esm/loader:178:24)
at async Object.loadESM (node:internal/process/esm_loader:68:5)
at async handleMainPromise (node:internal/modules/run_main:63:12)Now it's clear. We know that the timeout was set on line 5. Bluebird should be sufficient for Got:
import Bluebird from 'bluebird';
import got from 'got';
Bluebird.config({longStackTraces: true});
global.Promise = Bluebird;
try {
await got('https://httpbin.org/delay/1', {
timeout: {
request: 1
},
retry: {
limit: 0
}
});
} catch (error) {
console.error(error.stack);
}TimeoutError: Timeout awaiting 'request' for 1ms
at ClientRequest.<anonymous> (file:///home/szm/Desktop/got/dist/source/core/index.js:780:61)
at Object.onceWrapper (node:events:514:26)
at ClientRequest.emit (node:events:406:35)
at TLSSocket.socketErrorListener (node:_http_client:447:9)
at TLSSocket.emit (node:events:394:28)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
at Timeout.timeoutHandler [as _onTimeout] (file:///home/szm/Desktop/got/dist/source/core/timed-out.js:42:25)
at listOnTimeout (node:internal/timers:559:11)
at processTimers (node:internal/timers:500:7)
From previous event:
at new Promise (<anonymous>)
at asPromise (file:///home/szm/Desktop/got/dist/source/as-promise/index.js:21:21)
at lastHandler (file:///home/szm/Desktop/got/dist/source/create.js:42:27)
at iterateHandlers (file:///home/szm/Desktop/got/dist/source/create.js:49:28)
at got (file:///home/szm/Desktop/got/dist/source/create.js:69:16)
at file:///home/szm/Desktop/got/demo.js:8:8
at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
at async Loader.import (node:internal/modules/esm/loader:178:24)
at async Object.loadESM (node:internal/process/esm_loader:68:5)
at async handleMainPromise (node:internal/modules/run_main:63:12)As expected, we know where the timeout has been set. Unfortunately, if we increase our retry count limit to 1, the stack trace remains the same. That's because bluebird doesn't track I/O events. Please note that this should be sufficient for most cases. In order to debug further, we can use async_hooks instead. A Stack Overflow user has come up with an awesome solution:
import asyncHooks from 'node:async_hooks';
const traces = new Map();
asyncHooks.createHook({
init(id) {
const trace = {};
Error.captureStackTrace(trace);
traces.set(id, trace.stack.replace(/(^.+$\n){4}/m, '\n'));
},
destroy(id) {
traces.delete(id);
},
}).enable();
globalThis.Error = class extends Error {
constructor(message) {
super(message);
this.constructor.captureStackTrace(this, this.constructor);
}
static captureStackTrace(what, where) {
super.captureStackTrace.call(Error, what, where);
const trace = traces.get(asyncHooks.executionAsyncId());
if (trace) {
what.stack += trace;
}
}
};If we replace the bluebird part with this, we get:
/* Detailed source-code truncated for AI context efficiency. */This is extremely long, and not a complete Node.js app. Just a demo. Imagine how long it would be if this was used with databases, file systems, etc.
Conclusion
All these workarounds have a large impact on performance. However, there is a possible solution to this madness. Got provides handlers, hooks, and context. We can capture the stack trace in a handler, store it in a context and expose it in a beforeError hook.
import got from 'got';
const instance = got.extend({
handlers: [
(options, next) => {
Error.captureStackTrace(options.context);
return next(options);
}
],
hooks: {
beforeError: [
error => {
error.source = error.options.context.stack.split('\n');
return error;
}
]
}
});
try {
await instance('https://httpbin.org/delay/1', {
timeout: {
request: 100
},
retry: {
limit: 0
}
});
} catch (error) {
console.error(error);
}/* Detailed source-code truncated for AI context efficiency. */Yay! This is much more readable. Furthermore, we capture the stack trace only when got is called. This is definitely going to have some performance impact, but it will be much more performant than the other mentioned solutions.
Curious to know more? Check out these links:
- https://stackoverflow.com/questions/54914770/is-there-a-good-way-to-surface-error-traces-in-production-across-event-emitters
- https://github.com/nodejs/node/issues/11370
- https://github.com/puppeteer/puppeteer/issues/2037
- https://github.com/nodejs/node/pull/13870
Documentation Cache (`documentation/cache.md`)
Cache
Got implements RFC 7234 compliant HTTP caching which works out of the box in-memory and is easily pluggable with a wide range of storage adapters. Fresh cache entries are served directly from the cache, and stale cache entries are revalidated with If-None-Match / If-Modified-Since headers. You can read more about the underlying cache behavior in the cacheable-request documentation.
QUERY requests are not cached by Got's built-in cache option. Correctly caching QUERY requires cache keys that include the request content.
Requests with streaming or iterable bodies, including native FormData, bypass the cache. Got does not consume an upload to generate a cache key.
You can use the JavaScript Map type as an in-memory cache:
import got from 'got';
const map = new Map();
let response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> false
response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> trueGot uses Keyv internally to support a wide range of storage adapters. For something more scalable you could use an official Keyv storage adapter:
npm install @keyv/redisimport got from 'got';
import KeyvRedis from '@keyv/redis';
const redis = new KeyvRedis('redis://user:pass@localhost:6379');
await got('https://sindresorhus.com', {cache: redis});Got supports anything that follows the Map API, so it's easy to write your own storage adapter or use a third-party solution.
For example, the following are all valid storage adapters:
const storageAdapter = new Map();
await got('https://sindresorhus.com', {cache: storageAdapter});import storageAdapter from './my-storage-adapter';
await got('https://sindresorhus.com', {cache: storageAdapter});import QuickLRU from 'quick-lru';
const storageAdapter = new QuickLRU({maxSize: 1000});
await got('https://sindresorhus.com', {cache: storageAdapter});View the Keyv docs for more information on how to use storage adapters.
Advanced caching mechanisms
The request function may return an instance of IncomingMessage-like class.
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';
const getCachedResponse = (url, options) => {
const response = new Readable({
read() {
this.push("Hello, world!");
this.push(null);
}
});
response.statusCode = 200;
response.headers = {};
response.trailers = {};
response.socket = null;
response.aborted = false;
response.complete = true;
response.httpVersion = '1.1';
response.httpVersionMinor = 1;
response.httpVersionMajor = 1;
return response;
};
const instance = got.extend({
request: (url, options, callback) => {
return getCachedResponse(url, options);
}
});
const body = await instance('https://example.com').text();
console.log(body);
//=> "Hello, world!"If you don't want to alter the request function, you can return a cached response in a beforeRequest hook:
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';
const getCachedResponse = (url, options) => {
const response = new Readable({
read() {
this.push("Hello, world!");
this.push(null);
}
});
response.statusCode = 200;
response.headers = {};
response.trailers = {};
response.socket = null;
response.aborted = false;
response.complete = true;
response.httpVersion = '1.1';
response.httpVersionMinor = 1;
response.httpVersionMajor = 1;
return response;
};
const instance = got.extend({
hooks: {
beforeRequest: [
options => {
return getCachedResponse(options.url, options);
}
]
}
});
const body = await instance('https://example.com').text();
console.log(body);
//=> "Hello, world!"If you want to prevent duplicating the same requests, you can use a handler instead.
import got from 'got';
const map = new Map();
const instance = got.extend({
handlers: [
(options, next) => {
if (options.isStream) {
return next(options);
}
const pending = map.get(options.url.href);
if (pending) {
return pending;
}
const promise = next(options);
map.set(options.url.href, promise);
promise.finally(() => {
map.delete(options.url.href);
});
return promise;
}
]
});
const [first, second] = await Promise.all([
instance('https://httpbin.org/anything'),
instance('https://httpbin.org/anything')
]);
console.log(first === second);
//=> trueDocumentation Diagnostics Channel (`documentation/diagnostics-channel.md`)
Diagnostics Channel
Got integrates with Node.js diagnostic channels for low-overhead observability. Events are only published when subscribers exist.
Channels
`got:request:create`
Emitted when a request is created.
{requestId: string, url: string, method: string}`got:request:start`
Emitted before the native HTTP request is sent.
{requestId: string, url: string, method: string, headers: Record<string, string | string[] | undefined>}`got:response:start`
Emitted when response headers are received.
{requestId: string, url: string, statusCode: number, headers: Record<string, string | string[] | undefined>, isFromCache: boolean}`got:response:end`
Emitted when the response completes.
{requestId: string, url: string, statusCode: number, bodySize?: number, timings?: Timings}`got:request:retry`
Emitted when retrying a request.
{requestId: string, retryCount: number, error: RequestError, delay: number}`got:request:error`
Emitted when a request fails.
The error can be replaced by a beforeError hook. Use error instanceof RequestError before accessing Got-specific properties.
{requestId: string, url: string, error: Error, timings?: Timings}`got:response:redirect`
Emitted when following a redirect.
{requestId: string, fromUrl: string, toUrl: string, statusCode: number}Example
import diagnosticsChannel from 'node:diagnostics_channel';
const channel = diagnosticsChannel.channel('got:request:start');
channel.subscribe(message => {
console.log(`${message.method} ${message.url}`);
});All events for a single request share the same requestId.
TypeScript
All message types are exported from the main package:
import type {
DiagnosticRequestCreate,
DiagnosticRequestStart,
DiagnosticResponseStart,
DiagnosticResponseEnd,
DiagnosticRequestRetry,
DiagnosticRequestError,
DiagnosticResponseRedirect,
} from 'got';Documentation Lets Make A Plugin (`documentation/lets-make-a-plugin.md`)
Let's make a plugin!
Another example on how to use Got like a boss :electric_plug:
Okay, so you already have learned some basics. That's great!
When it comes to advanced usage, custom instances are really helpful.
For example, take a look at gh-got.
It looks pretty complicated, but... it's simple and extremely useful.
Before we start, we need to find the GitHub API docs.
Let's write down the most important information:
- The root endpoint is
https://api.github.com/. - We will use version 3 of the API.
TheAcceptheader needs to be set toapplication/vnd.github.v3+json. - The body is in a JSON format.
- We will use OAuth2 for authorization.
- We may receive
400 Bad Requestor422 Unprocessable Entity.
The body contains detailed information about the error. - Pagination? Yeah! Supported natively by Got.
- Rate limiting. These headers are interesting:
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
Also X-GitHub-Request-Id may be useful for debugging.
- The
User-Agentheader is required.
When we have all the necessary info, we can start mixing :cake:
The root endpoint
Not much to do here. Just extend an instance and provide the prefixUrl option:
import got from 'got';
const instance = got.extend({
prefixUrl: 'https://api.github.com'
});
export default instance;v3 API
GitHub needs to know which API version we are using. We'll use the Accept header for that:
import got from 'got';
const instance = got.extend({
prefixUrl: 'https://api.github.com',
headers: {
accept: 'application/vnd.github.v3+json'
}
});
export default instance;JSON body
We'll use options.responseType:
import got from 'got';
const instance = got.extend({
prefixUrl: 'https://api.github.com',
headers: {
accept: 'application/vnd.github.v3+json'
},
responseType: 'json'
});
export default instance;Authorization
It's common to set some environment variables, for example, GITHUB_TOKEN. You can modify the tokens in all your apps easily, right? Cool. What about... we want to provide a unique token for each app. Then we will need to create a new option - it will default to the environment variable, but you can easily override it.
Got performs option validation and doesn't know that token is a wanted option so it will throw. We can handle it inside an init hook and save it in options.context.
import got from 'got';
const instance = got.extend({
prefixUrl: 'https://api.github.com',
headers: {
accept: 'application/vnd.github.v3+json'
},
responseType: 'json',
context: {
token: process.env.GITHUB_TOKEN,
},
hooks: {
init: [
(raw, options) => {
if ('token' in raw) {
options.context.token = raw.token;
delete raw.token;
}
}
]
}
});
export default instance;For the rest we will use a handler. We could use hooks, but this way it will be more readable. Having beforeRequest, beforeError and afterResponse hooks for just a few lines of code would complicate things unnecessarily.
Tip:
- It's a good practice to use hooks when your plugin gets complicated.
- Try not to overload the handler function, but don't abuse hooks either.
import got from 'got';
const instance = got.extend({
prefixUrl: 'https://api.github.com',
headers: {
accept: 'application/vnd.github.v3+json'
},
responseType: 'json',
context: {
token: process.env.GITHUB_TOKEN,
},
hooks: {
init: [
(raw, options) => {
if ('token' in raw) {
options.context.token = raw.token;
delete raw.token;
}
}
]
},
handlers: [
(options, next) => {
// Authorization
const {token} = options.context;
if (token && !options.headers.authorization) {
options.headers.authorization = `token ${token}`;
}
return next(options);
}
]
});
export default instance;Errors
We should name our errors, just to know if the error is from the API response. Superb errors, here we come!
...
handlers: [
(options, next) => {
// Authorization
const {token} = options.context;
if (token && !options.headers.authorization) {
options.headers.authorization = `token ${token}`;
}
// Don't touch streams
if (options.isStream) {
return next(options);
}
// Magic begins
return (async () => {
try {
const response = await next(options);
return response;
} catch (error) {
const {response} = error;
// Nicer errors
if (response && response.body) {
error.name = 'GitHubError';
error.message = `${response.body.message} (${response.statusCode} status code)`;
}
throw error;
}
})();
}
]
...Note that by providing our own errors in handlers, we don't alter the ones in beforeError hooks.
The conversion is the last thing here.
Rate limiting
Umm... response.headers['x-ratelimit-remaining'] doesn't look good. What about response.rateLimit.limit instead?
Yeah, definitely. Since response.headers is an object, we can easily parse these:
const getRateLimit = (headers) => ({
limit: Number.parseInt(headers['x-ratelimit-limit'], 10),
remaining: Number.parseInt(headers['x-ratelimit-remaining'], 10),
reset: new Date(Number.parseInt(headers['x-ratelimit-reset'], 10) * 1000)
});
getRateLimit({
'x-ratelimit-limit': '60',
'x-ratelimit-remaining': '55',
'x-ratelimit-reset': '1562852139'
});
// => {
// limit: 60,
// remaining: 55,
// reset: 2019-07-11T13:35:39.000Z
// }Let's integrate it:
const getRateLimit = (headers) => ({
limit: Number.parseInt(headers['x-ratelimit-limit'], 10),
remaining: Number.parseInt(headers['x-ratelimit-remaining'], 10),
reset: new Date(Number.parseInt(headers['x-ratelimit-reset'], 10) * 1000)
});
...
handlers: [
(options, next) => {
// Authorization
const {token} = options.context;
if (token && !options.headers.authorization) {
options.headers.authorization = `token ${token}`;
}
// Don't touch streams
if (options.isStream) {
return next(options);
}
// Magic begins
return (async () => {
try {
const response = await next(options);
// Rate limit for the Response object
response.rateLimit = getRateLimit(response.headers);
return response;
} catch (error) {
const {response} = error;
// Nicer errors
if (response && response.body) {
error.name = 'GitHubError';
error.message = `${response.body.message} (${response.statusCode} status code)`;
}
// Rate limit for errors
if (response) {
error.rateLimit = getRateLimit(response.headers);
}
throw error;
}
})();
}
]
...The frosting on the cake: `User-Agent` header.
const packageJson = {
name: 'gh-got',
version: '12.0.0'
};
const instance = got.extend({
...
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': `${packageJson.name}/${packageJson.version}`
},
...
});Woah. Is that it?
Yup. View the full source code here. Here's an example of how to use it:
import ghGot from 'gh-got';
const response = await ghGot('users/sindresorhus');
const creationDate = new Date(response.created_at);
console.log(`Sindre's GitHub profile was created on ${creationDate.toGMTString()}`);
// => Sindre's GitHub profile was created on Sun, 20 Dec 2009 22:57:02 GMTPagination
import ghGot from 'gh-got';
const countLimit = 50;
const pagination = ghGot.paginate(
'repos/sindresorhus/got/commits',
{
pagination: {countLimit}
}
);
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);
for await (const commitData of pagination) {
console.log(commitData.commit.message);
}That's... astonishing! We don't have to implement pagination on our own. Got handles it all.
At the end
Did you know you can mix many instances into a bigger, more powerful one? Check out the Advanced Creation guide.
Documentation Quick Start (`documentation/quick-start.md`)
Quick Start Guide
Getting and posting data with promises
The simplest GET request:
import got from 'got';
const url = 'https://httpbin.org/anything';
const response = await got(url);The call returns a Promise<[Response](3-streams.md#response-2)>. If the body contains JSON, it can be retrieved directly:
import got from 'got';
const url = 'https://httpbin.org/anything';
const data = await got(url).json();The similar [got.text](1-promise.md#promisetext) method returns plain text.
All got methods accept an options object for passing extra configuration, such as headers:
import got from 'got';
const url = 'https://httpbin.org/anything';
const options = {
headers: {
'Custom-Header': 'Quick start',
},
timeout: {
send: 3500
},
};
const data = await got(url, options).json();A POST request is very similar:
import got from 'got';
const url = 'https://httpbin.org/anything';
const options = {
json: {
documentName: 'Quick Start',
},
};
const data = await got.post(url, options);The request body is passed in the options object. The json property will automatically set headers accordingly. Custom headers can be added exactly as above.
Using streams
The Stream API allows to leverage Node.js Streams capabilities:
import fs from 'node:fs';
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';
const url = 'https://httpbin.org/anything';
const options = {
json: {
documentName: 'Quick Start',
},
};
const gotStream = got.stream.post(url, options);
const outStream = fs.createWriteStream('anything.json');
try {
await streamPipeline(gotStream, outStream);
} catch (error) {
console.error(error);
}Options
Options can be set at the client level and reused in subsequent queries:
import got from 'got';
const options = {
prefixUrl: 'https://httpbin.org',
headers: {
Authorization: getTokenFromVault(),
},
};
const client = got.extend(options);
export default client;Some noticeable common options are:
searchParams: A query string object.prefixUrl: Prepended to query paths. Paths must be relative to prefix, i.e. not begin with a/.method: The HTTP method name.headers: Query headers.json: JSON body.form: A form query string object.
See the documentation for other options.
Errors
Both Promise and Stream APIs throw errors with metadata.
import got from 'got';
try {
const data = await got.get('https://httpbin.org/status/404');
} catch (error) {
console.error(error.response.statusCode);
}import got from 'got';
const stream = got.stream
.get('https://httpbin.org/status/404')
.once('error', error => {
console.error(error.response.statusCode);
});Miscellaneous
The HTTP method name can also be given as an option, this may be convenient when it is known only at runtime:
import got from 'got';
const url = 'https://httpbin.org/anything';
const method = 'POST';
const options = {
method,
json: {
documentName: 'Quick Start',
},
};
const data = await got(url, options);For most apps, HTTP clients just do GET and POST queries (PUT, PATCH or DELETE methods work similarly).
The following sections will give some pointers to more advanced usage.
Timeouts
By default, requests have no timeout. It is a good practice to set one:
import got from 'got';
const options = {
timeout: {
request: 10000,
},
};
const client = got.extend(options);
export default client;The above sets a global timeout of 10000 milliseconds for all requests issued by the exported client. Like all options, timeouts can also be set at the request level. See the timeout option.
Retries
A failed request is retried twice. The retry policy may be tuned with a retry options object.
import got from 'got';
const options = {
retry: {
limit: 5,
errorCodes: [
'ETIMEDOUT'
],
},
};Retries with stream are a little trickier, see stream.on('retry', …).
Hooks
Hooks are custom functions called on some request events:
import got from 'got';
const logRetry = (error, retryCount) => {
console.error(`Retrying after error ${error.code}, retry #: ${retryCount}`);
};
const options = {
hooks: {
beforeRetry: [
logRetry,
],
},
};
const client = got.extend(options);
export default client;Note that hooks are given as arrays, thus multiple hooks can be given. See documentation for other possible hooks.
Going further
There is a lot more to discover in the documentation and tips. Among others, Got can handle cookies, pagination, cache. Please read the documentation before implementing something that is already done by Got :innocent:.
Documentation Tips (`documentation/tips.md`)
Tips
Timeout
Each request can have a maximum allowed time to run.
In order to use this, specify the request timeout option.
import got from 'got';
const body = await got('https://httpbin.org/anything', {
timeout: {
request: 30000
}
});For more specific timeouts, visit the Timeout API.
Retries
By default, Got makes a new retry on a failed request if possible.
It is possible to disable this feature entirely by setting the amount of maximum allowed retries to 0.
import got from 'got';
const noRetryGot = got.extend({
retry: {
limit: 0
}
});In order to specify retriable errors, use the Retry API.
Cookies
Got supports cookies out of box. There is no need to parse them manually.
In order to use cookies, pass a CookieJar instance from the tough-cookie package.
import got from 'got';
import {CookieJar} from 'tough-cookie';
const cookieJar = new CookieJar();
await cookieJar.setCookie('foo=bar', 'https://httpbin.org');
await got('https://httpbin.org/anything', {cookieJar});Sessions
You can create a session by extending Got with a shared CookieJar. This automatically saves and reuses cookies between requests:
import got from 'got';
import {CookieJar} from 'tough-cookie';
const cookieJar = new CookieJar();
const sessionGot = got.extend({cookieJar});
// The cookie is saved within the cookie jar of the session
await sessionGot('http://httpbin.org/cookies/set?my_cookie=my_value');
await sessionGot('http://httpbin.org/cookies', {responseType: 'json', resolveBodyOnly: true});
//=> {cookies: {my_cookie: 'my_value'}}
// The cookies only apply to the session
await got('http://httpbin.org/cookies', {responseType: 'json', resolveBodyOnly: true});
//=> {cookies: {}}This also works with caching and other Got features.
AWS
Requests to AWS services need to have their headers signed.
This can be accomplished by using the got4aws package.
This is an example for querying an API Gateway with a signed request.
import got4aws from 'got4aws';
const got = got4aws();
const response = await got('https://<api-id>.execute-api.<api-region>.amazonaws.com/<stage>/endpoint/path', {
// …
});Pagination
When working with large datasets, it's very efficient to use pagination.
By default, Got uses the Link header to retrieve the next page.
However, this behavior can be customized, see the Pagination API.
const countLimit = 50;
const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', {
pagination: {countLimit}
});
console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);
for await (const commitData of pagination) {
console.log(commitData.commit.message);
}Measuring traffic usage
You can measure the total bytes transferred (sent and received) for a request by accessing the underlying socket's bytesWritten and bytesRead properties.
The socket is available via stream.socket or response.request.socket.
Note:
- The socket's byte counters include HTTP headers and all protocol overhead, not just the request/response body.
- The socket may be
undefinedwhen using cached responses.
import got from 'got';
const stream = got.stream('https://httpbin.org/json');
stream.on('response', () => {
console.log(`Downloaded: ${stream.socket.bytesRead} bytes`);
console.log(`Uploaded: ${stream.socket.bytesWritten} bytes`);
});
stream.on('end', () => {
const totalTraffic = stream.socket.bytesRead + stream.socket.bytesWritten;
console.log(`Total traffic: ${totalTraffic} bytes`);
});
stream.resume();You can also access the socket when using the Promise API:
import got from 'got';
const request = got('https://httpbin.org/json');
request.on('response', response => {
console.log(`Downloaded: ${response.request.socket.bytesRead} bytes`);
console.log(`Uploaded: ${response.request.socket.bytesWritten} bytes`);
});
const response = await request;
if (response.request.socket) {
const totalTraffic = response.request.socket.bytesRead + response.request.socket.bytesWritten;
console.log(`Total traffic: ${totalTraffic} bytes`);
}Rate limiting (throttling transfer speed)
Got doesn't include built-in bandwidth throttling, but you can implement it using stream transformers.
[!NOTE]
- To limit total transfer size (not rate), see the advanced-creation example.
- For production use, consider something like
bandwidth-throttle-stream.
import got from 'got';
import {Transform} from 'node:stream';
import {setTimeout} from 'node:timers/promises';
class ThrottleTransform extends Transform {
constructor(bytesPerSecond) {
super();
this.bytesPerSecond = bytesPerSecond;
}
async _transform(chunk, encoding, callback) {
const delay = (chunk.length / this.bytesPerSecond) * 1000;
if (delay > 0) {
await setTimeout(delay);
}
callback(null, chunk);
}
}
// Throttle download
got.stream('https://httpbin.org/bytes/1048576')
.pipe(new ThrottleTransform(100000)) // 100 KB/s
.pipe(process.stdout);For uploads, throttle the body stream before passing to Got:
import {createReadStream} from 'node:fs';
await got.post('https://httpbin.org/post', {
body: createReadStream('file.bin').pipe(new ThrottleTransform(100000))
});UNIX Domain Sockets
See the enableUnixSockets option.
Testing
Got uses the native http module, which depends on the native net module.
This means there are two possible ways to test:
- Use a mocking library like
nock, - Create a server.
The first approach should cover all common use cases.
Bear in mind that it overrides the native http module, so bugs may occur due to the differences.
The most solid way is to create a server.
There may be cases where nock won't be sufficient or lacks functionality.
Nock
By default nock mocks only one request.
Got will retry on failed requests by default, causing a No match for request ... error.
The solution is to either disable retrying (set options.retry.limit to 0) or call .persist() on the mocked request.
import got from 'got';
import nock from 'nock';
const scope = nock('https://sindresorhus.com')
.get('/')
.reply(500, 'Internal server error')
.persist();
try {
await got('https://sindresorhus.com')
} catch (error) {
console.log(error.response.body);
//=> 'Internal server error'
console.log(error.response.retryCount);
//=> 2
}
scope.persist(false);Proxying
Note:
- The popular
tunnelpackage is unmaintained. Use at your own risk.- The
proxy-agentfamily doesn't follow newest Node.js features and lacks support.
Although there isn't a perfect, bug-free package, Apify's solution is a modern one.
See got-scraping/src/agent/h1-proxy-agent.ts. It has the same API as hpagent.
hpagent is a modern package as well. In contrast to tunnel, it allows keeping the internal sockets alive to be reused.
import got from 'got';
import {HttpsProxyAgent} from 'hpagent';
await got('https://sindresorhus.com', {
agent: {
https: new HttpsProxyAgent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 256,
maxFreeSockets: 256,
scheduling: 'lifo',
proxy: 'https://localhost:8080'
})
}
});Alternatively, use global-agent to configure a global proxy for all HTTP/HTTPS traffic in your program.
Got's built-in HTTP/2 client does not include proxy support.
Retry without an agent
If you're using proxies, you may run into connection issues.
One way out is to disable proxies when retrying. The solution for the Stream API looks like this:
import https from 'node:https';
import fs from 'node:fs';
import got from 'got';
class MyAgent extends https.Agent {
createConnection(port, options, callback) {
console.log(`Connecting with MyAgent`);
return https.Agent.prototype.createConnection.call(this, port, options, callback);
}
}
const proxy = new MyAgent();
let writeStream;
const fn = retryStream => {
const options = {
agent: {
https: proxy,
}
};
const stream = retryStream ?? got.stream('https://example.com', options);
if (writeStream) {
writeStream.destroy();
}
writeStream = fs.createWriteStream('example-com.html');
stream.pipe(writeStream);
stream.once('retry', (retryCount, error, createRetryStream) => {
fn(createRetryStream({
agent: {
http: undefined,
https: undefined,
http2: undefined,
},
}));
});
};
fn();`h2c`
There is no direct h2c Upgrade support.
However, you can provide a h2session option in a beforeRequest hook. See an example.
Uppercase headers
Got always normalizes the headers, therefore passing an Uppercase-Header will transform it into uppercase-header. To fix this, you need to pass a wrapped agent:
class WrappedAgent {
constructor(agent) {
this.agent = agent;
}
addRequest(request, options) {
return this.agent.addRequest(request, options);
}
get keepAlive() {
return this.agent.keepAlive;
}
get maxSockets() {
return this.agent.maxSockets;
}
get options() {
return this.agent.options;
}
get defaultPort() {
return this.agent.defaultPort;
}
get protocol() {
return this.agent.protocol;
}
}
class TransformHeadersAgent extends WrappedAgent {
addRequest(request, options) {
const headers = request.getHeaderNames();
for (const header of headers) {
request.setHeader(this.transformHeader(header), request.getHeader(header));
}
return super.addRequest(request, options);
}
transformHeader(header) {
return header.split('-').map(part => {
return part[0].toUpperCase() + part.slice(1);
}).join('-');
}
}
const agent = new http.Agent({
keepAlive: true
});
const wrappedAgent = new TransformHeadersAgent(agent);See an example.
Custom options
Got v12 throws when an option does not exist. Therefore passing a top-level option such as:
import got from 'got';
await got('https://example.com', {
foo: 'bar'
});will throw. To prevent this, you need read the option in an init hook:
import got from 'got';
const convertFoo = got.extend({
hooks: {
init: [
(rawOptions, options) => {
if ('foo' in rawOptions) {
options.context.foo = rawOptions.foo;
delete rawOptions.foo;
}
}
]
}
});
const instance = got.extend(convertFoo, {
hooks: {
beforeRequest: [
options => {
options.headers.foo = options.context.foo;
}
]
}
});
const {headers} = await instance('https://httpbin.org/anything', {foo: 'bar'}).json();
console.log(headers.Foo); //=> 'bar'Eventually, you may want to create a catch-all instance:
import got from 'got';
const catchAllOptions = got.extend({
hooks: {
init: [
(raw, options) => {
for (const key in raw) {
if (!(key in options)) {
options.context[key] = raw[key];
delete raw[key];
}
}
}
]
}
});
const instance = got.extend(catchAllOptions, {
hooks: {
beforeRequest: [
options => {
// All custom options will be visible under `options.context`
options.headers.foo = options.context.foo;
}
]
}
});
const {headers} = await instance('https://httpbin.org/anything', {foo: 'bar'}).json();
console.log(headers.Foo); //=> 'bar'Note:
- It's a good practice to perform the validation inside the
inithook. You can safely throw when an option is unknown! Internally, Got uses the@sindresorhus/ispackage.
Electron `net` module is not supported
Note: Got v12 and later is an ESM package, but Electron does not yet support ESM. So you need to use Got v11.
Got doesn't support the electron.net module. It's missing crucial APIs that are available in Node.js.
While Got used to support electron.net, it got very unstable and caused many errors.
However, you can use IPC communication to get the Response object:
// Main process
const got = require('got');
const instance = got.extend({
// ...
});
ipcMain.handle('got', async (event, ...args) => {
const {statusCode, headers, body} = await instance(...args);
return {statusCode, headers, body};
});
// Renderer process
async () => {
const {statusCode, headers, body} = await ipcRenderer.invoke('got', 'https://httpbin.org/anything');
// ...
}Documentation Typescript (`documentation/typescript.md`)
TypeScript
Got is fully written in TypeScript, so the integration is seamless.
Furthermore, types have saved Got from many bugs and inconsistencies.
Wrapping Got in an API client
You can wrap Got in a type-safe API client while preserving all Got features including progress/events support:
import got, {type RequestPromise, type OptionsOfJSONResponseBody, type Progress} from 'got';
interface FizzBuzz {
fizz: boolean;
buzz: boolean;
}
class ExampleAPI {
private client = got.extend({
prefixUrl: 'https://api.example.com'
});
isFizzBuzz(number: number): RequestPromise<FizzBuzz> {
return this.client.get('fizzbuzz', {
searchParams: {number},
responseType: 'json',
resolveBodyOnly: true
}).json<FizzBuzz>();
}
// For more complex cases, you can specify options
getData(id: string, options?: OptionsOfJSONResponseBody): RequestPromise<FizzBuzz> {
return this.client.get(`data/${id}`, {
...options,
responseType: 'json',
resolveBodyOnly: true
}).json<FizzBuzz>();
}
}
const api = new ExampleAPI();
// The promise has full type support
const promise = api.isFizzBuzz(20);
// Event methods are available
promise.on('downloadProgress', (progress: Progress) => {
console.log(progress.percent);
});
// The result is properly typed
const result = await promise;
console.log(result.fizz); // boolean
console.log(result.buzz); // booleanNote:
- Use
.json<T>()to getRequestPromise<T>with the body-only response.- Use
.buffer()or.text()for other response types.- Without calling these methods, you'll get
RequestPromise<Response<T>>.- Use
signalandAbortControllerto abort requests.
Types
Here's a list of types that Got exports:
Note:
- The list may be incomplete. If you find a type is missing, please open an issue about it.
[`Got`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L200)
[`GotStream`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L195)
[`GotRequestFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L142)
[`GotPaginate`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L87)
[`OptionsWithPagination`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L82)
[`OptionsOfTextResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L74)
[`OptionsOfJSONResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L75)
[`OptionsOfBufferResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L76)
[`StrictOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L78)
[`StreamOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L79)
[`OptionsInit`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L535)
[`ExtendOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L55)
[`PlainResponse`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/response.ts#L6)
[`Response`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/response.ts#L95)
[`Request`](https://github.com/sindresorhus/got/blob/ecb05343dea3bd35933585a1ec5bcea01348d109/source/core/index.ts#L139)
[`RequestEvents`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/index.ts#L108)
[`Progress`](https://github.com/sindresorhus/got/blob/0f9f2b83b77710f2dc08c2a6bce1c78ba8d46760/source/core/index.ts#L40)
[`InstanceDefaults`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L17)
[`GotReturn`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L44)
[`HandlerFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L50)
[`RequestPromise`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/as-promise/types.ts#L26)
[`Delays`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/timed-out.ts#L14)
[`Timings`](https://github.com/sindresorhus/got/blob/main/source/core/utils/timer.ts#L19)
[`CreateConnectionFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L302)
[`CheckServerIdentityFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L303)
[`CacheOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L305)
[`HttpsOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L319)
[`PaginateData`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L382)
[`PaginationOptions`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L397)
[`SearchParameters`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L504)
[`ResponseType`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L518)
[`FilterData`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L388)
[`RetryObject`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L259)
[`RetryFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L267)
[`ParseJsonFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L235)
[`StringifyJsonFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L236)
[`Method`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L241)
[`ToughCookieJar`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L53)
[`PromiseCookieJar`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L60)
[`DnsLookupIpVersion`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L35)
[`RequestFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L43)
[`Agents`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L45)
[`Headers`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L51)
[`Hooks`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L75)
[`InitHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L65)
[`BeforeRequestHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L66)
[`BeforeRedirectHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L67)
[`BeforeErrorHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L68)
[`BeforeRetryHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L69)
[`AfterResponseHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L70)
Documentation Migration Guides Axios (`documentation/migration-guides/axios.md`)
Migration guides
You may think it's too hard to switch, but it's really not. 🦄
Axios
Axios is very similar to Got. The difference is that Axios targets browsers first, while Got fully makes use of Node.js features.
Common options
These options remain the same as well:
Renamed options
We deeply care about readability, so we renamed these options:
httpAgent→agent.httphttpsAgent→agent.httpssocketPath→urlresponseEncoding→encodingauth.username→usernameauth.password→passworddata→body/json/formparams→searchParams
Changes in behavior
transformRequest→hooks.beforeRequest- The API is different.
transformResponse→hooks.afterResponse- The API is different.
baseUrl→prefixUrl- The
prefixUrlis prepended to relative string input. Absolute URLs bypassprefixUrlby default. UseallowAbsoluteUrls: falseto reject absolute URLs whenprefixUrlis set.
- The
timeout- This option is now an object. You can now set timeouts on particular events!
responseType- Accepts
'text','json'or'buffer'.
- Accepts
Breaking changes
onUploadProgress- This option does not exist. Instead, use
got(…).on('uploadProgress', …).
- This option does not exist. Instead, use
onDownloadProgress- This option does not exist. Instead, use
got(…).on('downloadProgress', …).
- This option does not exist. Instead, use
maxContentLength- This option does not exist. Instead, use a handler.
validateStatus- This option does not exist. Got automatically validates the status according to the specification.
proxy- This option does not exist. You need to pass an
agentinstead.
- This option does not exist. You need to pass an
cancelToken- Use the
signaloption withAbortController.
- Use the
paramsSerializer- This option does not exist.
maxBodyLength- This option does not exist.
Response
The response object is different as well:
response.data→response.bodyresponse.status→response.statusCoderesponse.statusText→response.statusMessageresponse.config→response.request.optionsresponse.request- Returns a Got stream.
The response.headers object remains the same.
Interceptors
Got offers hooks instead, which are more flexible.
Errors
Errors look the same, with the difference error.request returns a Got stream. Furthermore, Got provides more details to make debugging easier.
Abort
Got supports AbortController via the signal option.
Convenience methods
Convenience methods, such as axios.get(…) etc. remain the same: got.get(…). Instead of axios.create(…) use got.extend(…).
You're good to go!
Well, you have already come this far :tada:
Take a look at the documentation. It's worth the time to read it.
There are some great tips.
If something is unclear or doesn't work as it should, don't hesitate to open an issue.
Documentation Migration Guides Nodejs (`documentation/migration-guides/nodejs.md`)
Migration guides
You may think it's too hard to switch, but it's really not. 🦄
Node.js
Let's make a simple request. With Node.js, this is:
import http from 'node:http';
const request = http.request('https://httpbin.org/anything', response => {
if (response.statusCode >= 400) {
request.destroy(new Error());
return;
}
const chunks = [];
let totalLength = 0;
response.on('data', chunk => {
chunks.push(chunk);
totalLength += chunk.length;
});
response.once('end', () => {
const bytes = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
bytes.set(chunk, offset);
offset += chunk.length;
}
if (response.statusCode >= 400) {
const error = new Error(`Unsuccessful response: ${response.statusCode}`);
error.body = new TextDecoder().decode(bytes);
return;
}
const text = new TextDecoder().decode(bytes);
console.log(text);
});
response.once('error', console.error);
});
request.once('error', console.error);
request.end();With Got, this becomes:
import got from 'got';
try {
const {body} = await got('https://httpbin.org/anything');
console.log(body);
} catch (error) {
console.error(error);
}Much cleaner. But what about streams?
import http from 'node:http';
import fs from 'node:fs';
const source = fs.createReadStream('article.txt');
const request = http.request('https://httpbin.org/anything', {
method: 'POST'
}, response => {
response.pipe(fs.createWriteStream('httpbin.txt'));
});
source.pipe(request);Well, it's easy as that:
import got from 'got';
import {pipeline as streamPipeline} from 'node:stream/promises';
import fs from 'node:fs';
await streamPipeline(
fs.createReadStream('article.txt'),
got.stream.post('https://httpbin.org/anything'),
fs.createWriteStream('httpbin.txt')
);The advantage is that Got also handles errors automatically, so you don't have to create custom listeners.
Furthermore, Got supports redirects, compression, advanced timeouts, cache, pagination, cookies, hooks, and more!
What next?
Unfortunately Got options differ too much from the Node.js options. It's not possible to provide a brief summary.
Don't worry, you will learn them fast - they are easy to understand! Every option has an example attached.
Take a look at the documentation. It's worth the time to read it.
There are some great tips.
If something is unclear or doesn't work as it should, don't hesitate to open an issue.
Documentation Migration Guides Request (`documentation/migration-guides/request.md`)
Migration guides
You may think it's too hard to switch, but it's really not. 🦄
Request
Let's take the very first example from Request's readme:
import request from 'request';
request('https://google.com', (error, response, body) => {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
});With Got, it is:
import got from 'got';
try {
const response = await got('https://google.com');
console.log('statusCode:', response.statusCode);
console.log('body:', response.body);
} catch (error) {
console.log('error:', error);
}Looks better now, huh? 😎
Common options
These Got options are the same as with Request:
urlbodyfollowRedirectencodingmaxRedirectslocalAddressheaderscreateConnection- UNIX sockets:
http://unix:SOCKET:PATH
The time option does not exist, assume it's always true.
So if you're familiar with these, you're good to go.
Renamed options
Note:
Readability is very important to us, so we have different names for these options:
qs→searchParamsstrictSSL→rejectUnauthorizedgzip→decompressjar→cookieJar(acceptstough-cookiejar)jsonReviver→parseJsonjsonReplacer→stringifyJson
Changes in behavior
- The
agentoption is now an object withhttp,httpsandhttp2properties. - The
timeoutoption is now an object. You can set timeouts on particular events! - The
searchParamsoption is always serialized usingURLSearchParams. - In order to pass a custom query string, provide it with the
urloption.got('https://example.com', {searchParams: {test: ''}})→https://example.com/?test=got('https://example.com/?test')→https://example.com/?test - To use streams, call
got.stream(url, options).
Breaking changes
- The
jsonoption is not aboolean, it's anobject. It will be stringified and used as a body. - The
formoption is anobjectand will be used asapplication/x-www-form-urlencodedbody. - All headers are converted to lowercase.
According to the spec, the headers are case-insensitive. - No
oauth/hawk/aws/httpSignatureoption.
To sign requests, you need to create a custom instance. - No
agentClass/agentOptions/pooloption. - No
foreveroption.
You need to pass an agent withkeepAliveoption set totrue. - No
proxyoption. You need to pass a custom agent. - No
authoption.
You need to useusername/passwordinstead or set theauthorizationheader manually. - No
baseUrloption.
Instead, there isprefixUrlwhich appends a trailing slash if not present. - No
removeRefererHeaderoption.
You can remove therefererheader in abeforeRequesthook. - No
followAllRedirectsoption. copyPipedHeadersdefaults tofalse.
Piped request headers are no longer copied automatically. Opt in withcopyPipedHeaders: truefor proxy scenarios.- With
copyPipedHeaders: true, explicitly set headers win over piped headers.
Piped headers only fill headers that were not explicitly set. - When copying piped headers, Got omits credentials, hop-by-hop headers, and headers nominated by
Connection/Proxy-Connection.
SeecopyPipedHeadersfor the full list.
Hooks are very powerful. Read more to see what else you achieve using hooks.
More about streams
Let's take a quick look at another example from Request's readme:
http.createServer((serverRequest, serverResponse) => {
if (serverRequest.url === '/doodle.png') {
serverRequest.pipe(request('https://example.com/doodle.png')).pipe(serverResponse);
}
});Request can proxy headers with the stream. Got can do that too, but it is opt-in:
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';
const server = http.createServer(async (serverRequest, serverResponse) => {
if (serverRequest.url === '/doodle.png') {
await streamPipeline(
serverRequest,
got.stream('https://example.com/doodle.png', {copyPipedHeaders: true}),
serverResponse
);
}
});
server.listen(8080);In terms of stream usage, nothing has really changed, but header proxying is opt-in via copyPipedHeaders: true.
Convenience methods
- If you were using
request.get,request.post, and so on - you can do the same with Got. - The
request.defaults({…})method has been renamed. You can do the same withgot.extend({…}). - There is no
request.cookie()norrequest.jar(). You have to usetough-cookiedirectly.
You're good to go!
Well, you have already come this far :tada:
Take a look at the documentation. It's worth the time to read it.
There are some great tips.
If something is unclear or doesn't work as it should, don't hesitate to open an issue.
--- METRICS ---
- Files Extracted: 21
- Estimated Token Budget: ~44124 tokens
- Canonical Reference: https://codewiki.google/github.com/sindresorhus/got