Skip to content

A non-prototype-mutating library for converting Node.js `http.ClientRequest` objects into curl command.

Notifications You must be signed in to change notification settings

panta82/node-curlifier

Repository files navigation

node-curlifier

A non-prototype-mutating library for converting Node.js http.ClientRequest objects into curl command.

Quick start

npm install node-curlifier

or

yarn add node-curlifier

or

pnpm add node-curlifier

then

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();

Stability

Used in production, but still not super battle-tested.

Motivation

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:

  1. It shims http.ClientRequest.prototype, which is yucky
  2. 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.

API

requestToCurl(req, callback?, timeout?)

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.js http.ClientRequest object.
  • callback? (error, curl?): Optional Node-style callback invoked when the request finishes or when the timeout fires.
  • timeout?: Controls how long to wait for req.end():
    • number: Custom timeout in milliseconds.
    • true or undefined: Use requestToCurl.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.

Usage with axios + http adapter

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.

Changelog

0.0.1

  • Initial release

License

MIT - See MIT.md for full license text.

About

A non-prototype-mutating library for converting Node.js `http.ClientRequest` objects into curl command.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published