Skip to content

Latest commit

 

History

608 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sync Request Curl

pipeline   codecov   Maintainability   Snyk Security   GitHub top language

NPM Version   Depfu Dependencies   FOSSA Status   NPM License   GitHub issues

Quality Gate Status   Codacy Badge   DeepSource   GitHub stars


A high-performance Node.js alternative to sync-request for making synchronous web requests.


1. Installation

npm install sync-request-curl

2. Usage

request(method, url, options);

The request function is the package default export. ESM consumers can import FormData and public types from the root entry. The /types subpath remains available explicitly:

import request, { FormData } from 'sync-request-curl';
import type { Options, Response } from 'sync-request-curl';
Examples (click to view)

GET request without options

import request from 'sync-request-curl';

const res = request('GET', 'https://comp1531namesages.alwaysdata.net');
console.log('Status Code:', res.statusCode);
const jsonBody = JSON.parse(res.body.toString());
console.log('Returned JSON object:', jsonBody);

GET request with query string parameters

import request from 'sync-request-curl';

const res = request('GET', 'https://comp1531forum.alwaysdata.net/echo/echo', {
  qs: { message: 'Hello, world!' },
});
console.log('Status Code:', res.statusCode);
const jsonBody = JSON.parse(res.body.toString());
console.log('Returned JSON object:', jsonBody);

POST request with headers and JSON payload

import request from 'sync-request-curl';

const res = request('POST', 'https://comp1531quiz.alwaysdata.net/quiz/create', {
  headers: { lab08quizsecret: "bruno's fight club" },
  json: {
    quizTitle: 'New Quiz',
    quizSynopsis: 'Sync request curl example',
  },
});

console.log('Status Code:', res.statusCode);
const jsonBody = JSON.parse(res.body.toString());
console.log('Returned JSON Object:', jsonBody);

POST request for file upload using multipart/form-data

import { readFileSync } from 'node:fs';
import request, { FormData } from 'sync-request-curl';

const form = new FormData();
form.append('example-file', readFileSync('./path/to/file.txt'), 'file.txt');
form.append('example-content', 'Example Content!');

const res = request('POST', 'https://example.com/upload', { form });
console.log('Status Code:', res.statusCode);

Proxy request

import request from 'sync-request-curl';

const res = request('GET', 'https://ipinfo.io/json', {
  proxy: {
    url: 'http://your-proxy-url:port',
    username: 'proxyUsername',
    password: 'proxyPassword',
  },
});

console.log('Status Code:', res.statusCode);
const jsonBody = res.getJSON();
console.log(jsonBody);

3. API reference

Request

request()

function request(
   method,
   url,
   options?
): Response;

Perform a synchronous HTTP(S) request and return the complete buffered response.

Parameters
Parameter Type Description
method HttpVerb Supported HTTP method. Matching is case-insensitive.
url string | URL Absolute http: or https: URL, provided as a string or URL.
options Options Request, transport, redirect, retry, and cache options.
Returns

Response

The buffered response after redirects and retries complete.


HttpVerb

type HttpVerb =
  | "GET"
  | "get"
  | "HEAD"
  | "head"
  | "POST"
  | "post"
  | "PUT"
  | "put"
  | "DELETE"
  | "delete"
  | "CONNECT"
  | "connect"
  | "OPTIONS"
  | "options"
  | "TRACE"
  | "trace"
  | "PATCH"
  | "patch"
  | "PROPFIND"
  | "propfind";

Supported HTTP methods. Input is case-insensitive and is normalised to uppercase before transport.


Options

type Options = {
  proxy?: ProxyOptions;
  rejectUnauthorized?: boolean;
  caFile?: string;
  localAddress?: string;
  localInterface?: string;
  tcpKeepAlive?: boolean
     | {
     idleSeconds?: number;
     intervalSeconds?: number;
   };
  cacheNamespace?: string;
  headers?: IncomingHttpHeaders;
  qs?: {
   [key: string]: unknown;
  };
  json?: JsonLike;
  body?: string | Buffer<ArrayBufferLike>;
  form?: FormData;
  timeout?: number;
  overallTimeout?: number;
  socketTimeout?: number;
  followRedirects?: boolean;
  maxRedirects?: number;
  allowRedirectHeaders?: string[];
  gzip?: boolean;
  cache?: "file" | "memory";
  agent?: boolean | Agent;
  retry?: boolean | RetryFunction;
  retryDelay?: number | RetryDelayFunction;
  maxRetries?: number;
};

Options accepted by request.

Payload precedence is form, then json, then body when more than one is supplied.

Type Declaration
Name Type Description
proxy? ProxyOptions Explicit HTTP/HTTPS proxy origin URL. Ambient proxy variables are ignored.
rejectUnauthorized? boolean Verify the origin certificate chain and hostname. Defaults to true.
caFile? string PEM CA bundle path for origin TLS verification.
localAddress? string Source IPv4/IPv6 address. Hostnames are rejected.
localInterface? string Source interface name. Mutually exclusive with localAddress.
tcpKeepAlive? | boolean | { idleSeconds?: number; intervalSeconds?: number; } Enable TCP keepalive, optionally with idle and interval controls.
cacheNamespace? string Private cache identity. Defaults to process.cwd().
headers? IncomingHttpHeaders Node-style request headers.
qs? { [key: string]: unknown; } Query values merged with any existing query string.
json? JsonLike JSON-compatible request body. Adds application/json when needed.
body? string | Buffer<ArrayBufferLike> Raw string or Buffer request body.
form? FormData Synchronous multipart/form-data body.
timeout? number Per-network-attempt timeout in milliseconds. 0 disables it.
overallTimeout? number Complete-operation deadline in milliseconds. 0 disables it.
socketTimeout? number Socket inactivity timeout in milliseconds. 0 disables it.
followRedirects? boolean Follow redirects automatically. Defaults to true.
maxRedirects? number Maximum redirects to follow. Negative or non-finite values mean no limit.
allowRedirectHeaders? string[] Caller headers allowed to be forwarded to redirect hops.
gzip? boolean Transparently decompress gzip/deflate responses. Defaults to enabled.
cache? "file" | "memory" Enable the private HTTP-aware cache in file or memory storage.
agent? boolean | Agent sync-request boolean agent option, or a keep-alive Node Agent.
retry? boolean | RetryFunction Retry GET requests, or provide a callback to decide per attempt.
retryDelay? number | RetryDelayFunction Retry delay in milliseconds, or a callback returning the delay.
maxRetries? number Maximum retry count. Defaults to 5 when retries are enabled.

JsonPrimitive

type JsonPrimitive = string | number | boolean | null;

Primitive JSON values accepted in request bodies.


NestedJsonLike

type NestedJsonLike =
  | JsonLike
  | undefined
  | {
  toJSON: () => NestedJsonLike;
};

Values accepted when nested inside JSON request bodies.


JsonLike

type JsonLike =
  | JsonPrimitive
  | readonly NestedJsonLike[]
  | {
[key: string]: NestedJsonLike;
}
  | {
  toJSON: () => JsonLike;
};

Values accepted for JSON request bodies.

This intentionally follows practical JSON.stringify() inputs rather than only strict JSON syntax. undefined is allowed inside objects and arrays, and objects with toJSON() (for example Date) are supported.


ProxyOptions

An explicit HTTP/HTTPS proxy and optional Basic credentials.

Properties
Property Type Description
url string HTTP/HTTPS proxy origin URL. May contain URL-encoded credentials.
username? string Overrides both URL credentials. An omitted password becomes an empty string.
password? string Proxy password. Requires an explicit username. Defaults to an empty string.

RetryResponse

Response shape passed to retry policy callbacks.

getBody() follows the same status handling as a normal response. Retry callbacks receive this buffered response before the next attempt begins.

Methods
getBody()
Call Signature
getBody(encoding): string;

Read the response body as a string using the requested encoding.

Parameters
Parameter Type
encoding BufferEncoding
Returns

string

Call Signature
getBody(): Buffer;

Read the response body as a Buffer.

Returns

Buffer

Properties
Property Type Description
statusCode number HTTP response status code.
headers IncomingHttpHeaders Node-style response headers with lowercase keys.
url string Final effective URL for the completed attempt.
body Buffer Buffered response body.

RetryFunction

type RetryFunction = (error, response, attemptNumber) => boolean;

Decide whether a GET request should be retried after an error or response.

attemptNumber starts at 1 for the first completed attempt.

Parameters
Parameter Type
error Error | null
response RetryResponse | undefined
attemptNumber number
Returns

boolean


RetryDelayFunction

type RetryDelayFunction = (error, response, attemptNumber) => number;

Return the delay in milliseconds before the next retry.

attemptNumber starts at 1 for the first completed attempt.

Parameters
Parameter Type
error Error | null
response RetryResponse | undefined
attemptNumber number
Returns

number

Response

GetBody

type GetBody = {
  <Encoding>(encoding): string;
  (): Buffer;
};

Read the current response body.

Calling without an encoding returns the Buffer. Passing an encoding returns a string. A response with statusCode >= 300 throws ResponseError.

Call Signature
<Encoding>(encoding): string;
Type Parameters
Type Parameter
Encoding extends BufferEncoding
Parameters
Parameter Type
encoding Encoding
Returns

string

Call Signature
(): Buffer;
Returns

Buffer


GetJSON

type GetJSON = <T>(encoding?) => T;

Parse the current response body as JSON.

Unlike GetBody, this helper does not reject HTTP error status codes. It only throws if the body cannot be parsed as JSON. Defaults to any for v4 compatibility. Pass an explicit type argument to describe the expected result; this does not perform runtime validation.

Type Parameters
Type Parameter Default type
T any
Parameters
Parameter Type
encoding? BufferEncoding
Returns

T


Response

Buffered synchronous response returned by request.

Helper methods observe later mutations to the public response object rather than a hidden immutable snapshot.

Properties
Property Type Description
getBody GetBody Read the response body and throw ResponseError for HTTP status >= 300.
getJSON GetJSON Parse the response body as JSON without applying HTTP status handling.
statusCode number HTTP response status code.
headers IncomingHttpHeaders Node-style response headers with lowercase keys.
url string Final effective URL after query handling and redirects.
body Buffer<ArrayBufferLike> Mutable buffered response body.

BufferEncoding

type BufferEncoding =
  | "base64"
  | "ascii"
  | "utf8"
  | "utf-8"
  | "utf16le"
  | "ucs2"
  | "ucs-2"
  | "base64url"
  | "latin1"
  | "binary"
  | "hex";

Buffer encodings accepted by response body helpers.

Multipart

FormDataEntry

One multipart entry accepted by FormData.

Properties
Property Type Description
key string Multipart field name.
value string | Buffer<ArrayBufferLike> | Blob Text, Buffer, or Blob field value.
fileName? string Optional file name. Path components are stripped before sending.

FormData

Synchronous multipart/form-data builder compatible with sync-request.

Pass an instance through the request form option.

Constructors
Constructor
new FormData(): FormData;
Returns

FormData

Methods
append()
append(
   key,
   value,
   fileName?
): void;

Append a text, Buffer, or Blob field.

When fileName is supplied, its basename is used and the media type is inferred from the extension with an application/octet-stream fallback. Blob media types remain authoritative. Blob reads throw if the reader fails or does not finish within 30 seconds.

Parameters
Parameter Type
key string
value string | Buffer<ArrayBufferLike> | Blob
fileName? string
Returns

void

Errors

RequestErrorCode

type RequestErrorCode =
  | "ERR_INVALID_URL"
  | "ENOTFOUND"
  | "ETIMEDOUT"
  | "ERR_TOO_MANY_REDIRECTS"
  | "ERR_REQUEST_FAILED";

Stable transport-neutral error codes emitted by the TypeScript request layer.


CurlError

Raw libcurl transport failure.

The numeric code is retained for compatibility with earlier sync-request-curl releases and maps to libcurl's documented error codes.

Extends
  • Error
Constructors
Constructor
new CurlError(code, message): CurlError;
Parameters
Parameter Type
code number
message string
Returns

CurlError

Overrides
Error.constructor
Properties
Property Type Description
code number Numeric libcurl error code.

RequestError

Transport-neutral request failure created by the TypeScript request layer.

Extends
  • Error
Constructors
Constructor
new RequestError(
   code,
   message,
   options?
): RequestError;
Parameters
Parameter Type
code RequestErrorCode
message string
options? { cause?: unknown; }
options.cause? unknown
Returns

RequestError

Overrides
Error.constructor
Properties
Property Modifier Type Description
code readonly RequestErrorCode Stable transport-neutral request error code.

ResponseError

HTTP status error thrown by response.getBody() for status codes >= 300.

The status, headers, and body that produced the error remain available on the error object.

Extends
  • Error
Constructors
Constructor
new ResponseError(
   statusCode,
   headers,
   body,
   encoding?
): ResponseError;
Parameters
Parameter Type
statusCode number
headers IncomingHttpHeaders
body Buffer
encoding? BufferEncoding
Returns

ResponseError

Overrides
Error.constructor
Properties
Property Modifier Type Description
statusCode readonly number HTTP status code that caused the error.
headers readonly IncomingHttpHeaders Response headers returned by the server.
body readonly Buffer Buffered response body returned by the server.

4. Differences from sync-request

4.1. Additions

  • Response#getJSON() is available as a convenience helper.
  • cache: "memory" is available as an alternative to the file cache.
  • retry and retryDelay can be callbacks when you need to decide retry behaviour at runtime.
  • agent still accepts the boolean values supported by sync-request, and can also take a keep-alive Node Agent for connection reuse.
  • overallTimeout sets a deadline for the whole operation, alongside the per-attempt timeout and inactivity socketTimeout options.
  • Proxy, TLS, local network binding, and TCP keepalive have dedicated options.

4.2. Behavioural differences

  • RFC 9110 defines request framing independently of the method, so request content is permitted on GET, DELETE, and HEAD. The standard also notes that this content has no generally defined semantics and may be rejected by some implementations.
  • Falsy JSON values such as false, 0, "", and null are valid payloads.
  • Invalid HTTP framing is rejected rather than sending conflicting Content-Length and Transfer-Encoding headers.
  • Only absolute http: and https: URLs are accepted. Proxy environment variables are ignored. Use the proxy option when proxying a request.
  • An explicit Authorization header takes precedence over credentials in the URL, and a caller-supplied Accept-Encoding header is left unchanged.
  • 307 and 308 redirects preserve the request method and body. sync-request can rewrite some body-bearing redirects to GET.
  • Query merging preserves additional literal ? and # delimiters that sync-request can truncate while splitting URLs.
  • Cache handling is stricter: no-store takes precedence, Age is updated on cache hits, cached headers are isolated from mutation, and recoverable cache-read errors are treated as misses.
  • HTTPS requests can negotiate HTTP/2 automatically when supported.

5. License

MIT License

6. Compatibility

sync-request-curl supports Node.js 16.17.0 and newer.

The package manager selects a matching native binary when one is available. Installing or importing the package does not compile native code.

6.1. Windows

Prebuilt binaries are available for x64, arm64, and x86 (ia32) Windows. For x86, use a Node.js release that provides an x86 runtime.

Requests can fail with Libcurl Error 60 (CURLE_PEER_FAILED_VERIFICATION) when the peer certificate cannot be verified. rejectUnauthorized: false disables origin certificate and hostname verification and should only be used when that trade-off is intentional.

6.2. macOS

Prebuilt binaries are available for Apple Silicon (arm64) and Intel (x64) macOS.

6.3. Linux

Prebuilt binaries are available for x64 and arm64 Linux on both glibc and musl. GNU/Linux release binaries require GLIBC 2.31 or newer.

6.4. Building from source

If a prebuilt binary is unavailable for your platform, or if optional dependencies were intentionally omitted, build the installed package explicitly using your package manager:

npm exec --no -- sync-request-curl-build
pnpm exec sync-request-curl-build
yarn run sync-request-curl-build

Run the build with the same Node.js architecture that will use the library. Run sync-request-curl-build --help for the current prerequisites.

Source builds keep the release defaults: macOS links the system libcurl, while Linux and Windows build the libcurl bundled by curl-sys. Override that choice explicitly when needed:

npm exec --no -- sync-request-curl-build --libcurl=system
npm exec --no -- sync-request-curl-build --libcurl=bundled

--libcurl=system is strict: if curl-sys cannot discover a compatible system libcurl, the build fails instead of silently falling back to its bundled copy. On Unix systems, system discovery uses the platform libcurl or pkg-config. On Windows, curl-sys uses vcpkg. System builds inherit the capabilities and TLS behaviour of the selected libcurl. --libcurl=bundled uses the pinned libcurl shipped by curl-sys and retains the package's vendored build configuration. The flag selects the libcurl implementation. Both modes continue to use curl-sys as the Rust FFI layer.

Source builds require:

  • Rust 1.88 or newer and Cargo
  • Linux and other Unix systems: a C/C++ compiler, make, Perl, pkg-config, and CA certificates
  • macOS: Xcode Command Line Tools
  • Windows: Visual Studio C++ Build Tools and the Windows SDK for the target CPU
  • Access to the locked Cargo dependencies, or an already populated Cargo cache

Other architectures and Unix platforms may work when Node.js, Rust, and the required native dependencies support them, but they are not part of the prebuilt release matrix.

Set CARGO_BUILD_TARGET when you need to select a Rust target explicitly. The build must still run with a Node.js architecture compatible with the resulting addon.

To use an externally managed native build, set SYNC_REQUEST_CURL_NATIVE_PATH to the absolute path of its .node file.

7. Caveats

sync-request-curl was developed to improve performance with sending synchronous requests in Node.js. It is also free from the sync-request bug which leaves an orphaned sync-rpc process, resulting in a leaked handle being detected in Jest.

sync-request-curl was initially designed to work with UNIX-like systems for UNSW students enrolled in COMP1531 Software Engineering Fundamentals. The native distribution targets glibc- and musl-based Linux, Windows, and macOS on the architectures listed in the compatibility section.

Please note that this library's primary goal is to simplify the learning of JavaScript for novice programmers, hence its synchronous nature. However, we recommend to always use an asynchronous alternative where possible.

Releases

Used by

Contributors

Languages