npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

requestcodegen

v0.1.1

Published

Parse curl commands and raw HTTP requests, and generate HTTP client code in different programming languages.

Readme

requestcodegen

Build NPM package

requestcodegen parses curl commands and raw HTTP requests, and generates HTTP client code in different programming languages.

It is synchronous, has no runtime dependencies and works offline in browsers and Node.js 18+. It never executes commands, reads files or makes network requests.

Install

npm install requestcodegen

Use

import {
    parse,
    generateCode,
    CodeTarget,
    ParseError,
    GeneratorError,
} from "requestcodegen";

// curl -> request -> code
const request = parse.curlCommand(
    "curl 'https://example.com/items' -H 'Accept: application/json'"
);
const python = generateCode(request, CodeTarget.PythonRequests);

// Requests are plain JSON
const requestJson = JSON.stringify(request, null, 2);
const curl = generateCode(JSON.parse(requestJson), CodeTarget.Curl);

// Raw HTTP -> curl
const curlFromHttp = generateCode(
    parse.httpRequest("GET /items HTTP/1.1\r\nHost: example.com\r\n\r\n"),
    CodeTarget.Curl
);

// Errors tell you which step failed and have a stable code
try {
    const code = generateCode(parse.curlCommand(input), CodeTarget.JavaScript);
} catch (error) {
    if (error instanceof ParseError) {
        console.log(error.code, error.message); // e.g. UNSUPPORTED_INPUT for -F
    } else if (error instanceof GeneratorError) {
        console.log(error.code, error.message); // e.g. UNSUPPORTED_TARGET
    }
}

API

parse.curlCommand(command: string): RequestOptions;
parse.httpRequest(input: string, baseUrl?: string): RequestOptions;
generateCode(request: RequestOptions, target: CodeTarget): string;

generateCode validates the request and never mutates it.

Request format

The request is a plain object, and JSON documents with this shape are accepted as is:

interface RequestOptions {
    url: string; // absolute http:// or https:// URL
    method?: string; // defaults to GET
    query?: Record<string, string | string[]>;
    headers?: Record<string, string>;
    body?: string;
    followRedirects?: boolean;
    compressed?: boolean;
}
{
    "url": "https://example.com/items?sort=asc",
    "method": "POST",
    "query": { "tag": ["a", "b"] },
    "headers": { "Content-Type": "application/json" },
    "body": "{\"name\":\"John\"}",
    "followRedirects": true
}
  • url: the existing query is kept exactly as written, including duplicate keys. The fragment is dropped, because it is never sent. Spaces and non-ASCII characters are percent-encoded.
  • query: appended to the URL once, encoded as application/x-www-form-urlencoded (the same as URLSearchParams). Arrays become repeated keys (tag=a&tag=b), and empty arrays add nothing.
  • headers: names are compared case-insensitively. Case variants of the same name, non-string values and line breaks or other control characters are rejected. Authentication and cookies are plain Authorization and Cookie headers.
  • body: UTF-8 text sent exactly as is. Generators never parse or re-serialize it. "" is an empty body and undefined is no body.
  • followRedirects, compressed: omitted means the client's default behavior. See targets.

Errors

The parsers throw ParseError and generateCode throws GeneratorError. Both have a code and, when relevant, a field. Messages name the option or field, but never include header values, credentials or bodies.

| Class | Code | Meaning | | - | - | - | | ParseError | INVALID_INPUT | Malformed input: bad URL, header, method, quoting or request line. | | ParseError | UNSUPPORTED_INPUT | Valid input this release does not support, for example -F, files, pipes or chunked bodies. | | ParseError | BASE_URL_REQUIRED | A raw HTTP request with a path target and no Host header needs baseUrl. | | GeneratorError | INVALID_REQUEST | The request object is invalid: bad URL, method, header, query or body. | | GeneratorError | UNSUPPORTED_TARGET | Unknown target, or a request the target's client cannot send as specified. |

curl parsing

parse.curlCommand accepts one Bash-style command starting with curl or curl.exe. It supports what browser developer tools produce with "Copy as cURL (bash)", including:

  • single and double quotes, escapes, $'...' strings (with \xHH UTF-8 bytes) and \ line continuations;
  • options before or after the URL, --, combined short options (-sSL), attached values (-XPOST) and --option=value;
  • a leading $ prompt.

Shell variables such as $API_TOKEN stay literal text. Command substitution, pipes, redirects, ;, &&, &, brace expansion and multiple commands are rejected with UNSUPPORTED_INPUT. A URL without a scheme gets http://, like curl.

| Options | Result | | - | - | | -X, --request | Method. | | -I, --head | HEAD. | | --url, positional URL | URL. Only one URL is supported. | | -H, --header | Header. Name; sends an empty header. Repeated names are combined with , (; for Cookie). Name: (removing a curl default) is rejected. | | -d, --data, --data-ascii, --data-binary, --data-raw | Body, joined with &. A leading @ is a file except with --data-raw. | | --data-urlencode | Encoded like curl: content, =content, name=content. | | --json | Body plus Content-Type and Accept: application/json defaults. The JSON is not validated or reformatted. | | -G, --get | Data is appended to the query instead. | | -u, --user, --basic, credentials in the URL | Authorization: Basic .... A missing password (a prompt) is rejected. | | --oauth2-bearer | Authorization: Bearer .... | | -b, --cookie | Cookie header (name=value only; a file name is rejected). | | -A, --user-agent, -e, --referer | User-Agent, Referer. | | -L, --location, --no-location | followRedirects: true / false. | | --compressed, --no-compressed | compressed: true / omitted. | | -g, --globoff | URL globbing off. Without it, {a,b} and [1-9] patterns are rejected; other brackets are kept literally. | | -s, -S, -v, -i, -o, -O, -w, -#, -f, --fail-with-body, -m, --connect-timeout, --retry, --http1.1, --http2, -N, --no-progress-meter | Output and transfer options. Accepted and omitted from generated code. |

Curl's precedence rules apply: the method comes from -X, then -I, then data (POST), else GET. Explicit -H headers win over -u, -A, -e, -b and the --json and -d content type defaults. Data sets Content-Type: application/x-www-form-urlencoded unless a content type is given.

Everything else is rejected: unknown options, -F/--form, -T, -K, -k, --next, other auth methods, file and stdin references, non-HTTP URLs and multiple URLs.

Raw HTTP parsing

parse.httpRequest accepts HTTP/1.0 and HTTP/1.1 request text with LF or CRLF line endings, as copied from browser developer tools.

  • The request target can be an absolute URL or a path. A path is resolved against baseUrl (only its origin is used) or, without baseUrl, against https:// plus the Host header. If neither is available, it throws BASE_URL_REQUIRED. A target starting with // never changes the host.
  • Host must match the URL. Host and Content-Length are removed from the headers because clients compute them.
  • The body starts after the first blank line and is kept byte for byte, including trailing newlines. A request with no body text and Content-Length: 0 gets an empty-string body.
  • Repeated headers are combined like in curl parsing.
  • Chunked Transfer-Encoding, folded header lines, HTTP/2 pseudo-headers, CONNECT, OPTIONS * and binary data are rejected.

Targets

These targets are verified end to end against a local server, including redirects, compressed, JSON, text and empty responses:

| Target | Output | Requirements | | - | - | - | | Curl | Bash curl command | curl | | PythonRequests | Python | pip install requests | | JavaScript | Browser fetch(), ES module with top-level await | A modern browser | | NodeFetch | Native Node.js fetch(), ES module (.mjs) with top-level await | Node.js 18+ | | NodeAxios | Axios, ES module (.mjs) with top-level await | npm install axios (1.x) | | PHPCurl | PHP curl extension | PHP with ext-curl | | PHPGuzzle | Guzzle | composer require guzzlehttp/guzzle (7.x) |

Examples assign the response to response ($response in PHP) and do not assume it is JSON.

| Target | followRedirects | compressed | | - | - | - | | Curl | true adds -L | true adds --compressed | | PythonRequests | allow_redirects when it differs from the default (Requests follows, except for HEAD) | Requests asks for compression by default; false sends Accept-Encoding: identity | | JavaScript | false sets redirect: "manual" (browsers return an opaque response that cannot be inspected) | Browsers always negotiate compression; false is rejected | | NodeFetch | false sets redirect: "manual" | Enabled by default; false sends Accept-Encoding: identity | | NodeAxios | false sets maxRedirects: 0 (Axios then rejects 3xx responses) | Enabled by default; false sends Accept-Encoding: identity | | PHPCurl | true sets CURLOPT_FOLLOWLOCATION | true sets CURLOPT_ENCODING => '' | | PHPGuzzle | false sets allow_redirects => false | true sets decode_content |

An explicit Accept-Encoding header is always sent as given.

Target limitations:

  • JavaScript (browser): headers a browser controls itself are left out: Cookie, Origin, Referer, Host, Connection, Accept-Encoding, Content-Length, Sec-*, Proxy-* and the other forbidden request headers. A Cookie header becomes credentials: "include", so the browser sends its own cookies.
  • JavaScript, NodeFetch: a body with GET or HEAD, credentials in the URL, and a Host header for a different host are rejected. NodeFetch also rejects Keep-Alive, Transfer-Encoding, Upgrade and Expect headers, which Node.js refuses.
  • JavaScript, NodeFetch, NodeAxios: header values must be ASCII, because these clients cannot send UTF-8 header values. Python sends them as UTF-8 bytes, and curl and PHP send them as is.
  • PythonRequests: methods are sent in upper case. Bodies are sent unchanged with data=.
  • JavaScript, NodeFetch, NodeAxios: a JSON body is shown as an object literal only when JSON.stringify reproduces the exact same text. Otherwise it stays a string, and Axios gets transformRequest so the text is not re-serialized.
  • Curl, PHPCurl: like curl itself, a body without Content-Type is sent as application/x-www-form-urlencoded.

Other targets

Clojure, CSharp, Dart, Elixir, Go, Java, Kotlin, NodeHTTP, ObjectiveC, PHP (stream contexts), PHPRequests, Python (urllib), Ruby, Rust, Swift and Wget use the same validation, query handling and escaping. Go, Java, Swift, Ruby, Python (urllib), Node HTTP, PHP and PHP Requests are also run end to end in this repository's tests.

They generate each client's default redirect and compression behavior and throw UNSUPPORTED_TARGET for an explicit followRedirects or compressed value that differs from it:

| Follows redirects by default | Does not | | - | - | | Clojure, C#, Dart, Go, Java, Kotlin, Objective-C, PHP, PHP Requests, Python, Rust, Swift, Wget | Elixir, Node HTTP, Ruby |

| Requests compression by default | Does not | | - | - | | Clojure, Dart, Go, Objective-C, PHP Requests, Ruby, Swift | C#, Elixir, Java, Kotlin, Node HTTP, PHP, Python, Rust, Wget |

Other limitations:

  • Java and Kotlin (HttpURLConnection) reject methods other than GET, POST, HEAD, OPTIONS, PUT, DELETE and TRACE. For headers it normally drops (Origin, Connection, Host, ...), the code sets sun.net.http.allowRestrictedHeaders.
  • Python (urllib), Node HTTP and Swift reject non-ASCII header values.
  • PHP Requests rejects empty header values, which it would not send.
  • C# rejects Content-* headers without a body.

Compatibility

generateCode, RequestOptions and every existing CodeTarget name and value are unchanged, CodeTarget.PHP is still the stream implementation, and calls like generateCode({ url }, CodeTarget.Curl) still work. JsonBody was removed: pass JSON.stringify(value) as the body and set Content-Type: application/json.

Generated code changed on purpose to fix incorrect output.

Development

npm install
npm test             # unit tests
npm run typecheck
npm run build        # dist/: ESM, CommonJS and type declarations
npm run test:package # packs the library and imports it from ESM, CommonJS, TypeScript and a browser bundle
npm run examples     # regenerates examples.md

npm run test:e2e runs generated code against a local server. It needs curl, Node.js, Docker (PHP, Composer and Ruby images), Go, Java and Swift, plus a Python virtual environment and PHP dependencies in node_modules/.cache/e2e:

python3 -m venv node_modules/.cache/e2e/venv
node_modules/.cache/e2e/venv/bin/pip install requests
mkdir -p node_modules/.cache/e2e/php
docker run --rm -v "$PWD/node_modules/.cache/e2e/php":/app -w /app composer:2 require guzzlehttp/guzzle:^7 rmccue/requests:^2

To publish, bump the version in package.json, update the changelog and run npm publish. prepublishOnly type-checks, tests, builds and verifies the packed package.

Code examples

Check out examples of generated code in the examples.md file.

Known usages

  1. ScreenshotOne uses the library for generating code examples in the playground for the screenshot API.

If you use the library, please, don't hesitate to share how and in what project.

License

krasun/requestcodegen is released under the MIT license.