A non-prototype-mutating library for converting Node.js http.ClientRequest objects into curl command.
npm install node-curlifieror
yarn add node-curlifieror
pnpm add node-curlifierthen
import http from 'http';
import { requestToCurl } from 'node-curlifier';
const req = http.request('https://api.example.com/users', res => {
// Handle response...
});
requestToCurl(req).then(curl => {
console.log(curl);
// Output: curl 'https://api.example.com/users' --compressed
});
req.end();Used in production, but still not super battle-tested.
Module request-to-curl is a pretty handy way to get copy-pastable curl commands to debug network issues.
But it comes with 2 problems:
- It shims
http.ClientRequest.prototype, which is yucky - It buffers all requests, which can lead to memory pressure and leaks in cases like HTTP streaming or big file uploads.
This library gives you more control by only shimming individual requests that you want to log. No prototypes are modified. This allows you to decide where the performance tradeoffs make sense.
Attaches lightweight shims to the provided request object so it can buffer anything written through write() or end(), and resolves as soon as the caller invokes req.end(). Returns a promise that resolves with the curl string, rejects with a timeout error. Callback interface is also supported, which might be more convenient for something like simple logging.
Parameters
req: A Node.jshttp.ClientRequestobject.callback? (error, curl?): Optional Node-style callback invoked when the request finishes or when the timeout fires.timeout?: Controls how long to wait forreq.end():number: Custom timeout in milliseconds.trueorundefined: UserequestToCurl.DEFAULT_TIMEOUT_MS(30s by default).false: Disable the timeout and wait indefinitely.
When the timeout triggers, the returned promise rejects with { code: 'ETIMEDOUT' } and the callback receives the same error as its first argument.
// Using a callback
requestToCurl(req, (err, curl) => {
if (err) {
console.error('Failed to capture curl', err);
return;
}
console.log(curl);
});
// Custom timeout (5 seconds)
const curl = await requestToCurl(req, undefined, 5000);
// Disable timeout
const slowCurl = await requestToCurl(req, undefined, false);Best-effort behavior: if you call requestToCurl after the request has already finished, the library will use any buffered output (for example Node's outputData queue) so you still get a useful command without throwing.
Axios is a bit inconvenient to work with, because it attaches its own headers and does other processing after calling interceptors, but before sending the request. That's why curl loggers that rely on interceptors (eg. axios-curlirize, axios2curl) won't produce the correct results.
You can use this library with axios by producing a transport shim like this:
import followRedirects, { FollowOptions } from 'follow-redirects';
const makeTransport = (config: AxiosRequestConfig) => {
const url = new URL(config.url, config.baseURL);
const transport = /https:?/.test(url.protocol) ? followRedirects.https : followRedirects.http;
// NOTE: Axios only uses transport.request, so we don't need anything else
return {
request: (requestOptions: FollowOptions<RequestOptions>, handleResponse) => {
if (config.maxRedirects) {
requestOptions.maxRedirects = config.maxRedirects;
}
const req = transport.request(requestOptions, handleResponse);
requestToCurl(req['_currentRequest'], (err, curl) => {
if (curl) {
console.log(`[REQ] ${curl}`);
}
});
return req;
},
};
};
const config = {
url: 'https://example.com/api',
};
const resp = await axiosInstance({
...config,
transport: makeTransport(config),
});
//...This pattern is still experimental, which is why it's not officially in the library.
- Initial release
MIT - See MIT.md for full license text.