- 1. Installation
- 2. Usage
- 3. API reference
- 4. Differences from sync-request
- 5. License
- 6. Compatibility
- 7. Caveats
npm install sync-request-curl
request(method, url, options);The request function is the package default export. ESM consumers can import
FormData and public types from the root entry:
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);FormData also exposes the synchronous Node form-data helpers used by
then-request: getHeaders(), getBoundary(), setBoundary(), getBuffer(),
getLengthSync(), hasKnownLength(), and toString(). The append() options
object supports filename, contentType, knownLength, and the advanced raw
header override. A custom header is serialized verbatim and is responsible
for its own multipart boundary and part headers. Stream-valued fields and
callback/stream helpers such as getLength(), pipe(), and submit() are not
provided.
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);function request(
method,
url,
options?
): Response;Perform a synchronous HTTP(S) request and return the complete buffered response.
| Parameter | Type | Description |
|---|---|---|
method |
HttpVerb |
Recognised HTTP method. Matching is case-insensitive; CONNECT is rejected. |
url |
string | URL |
Absolute http: or https: URL, provided as a string or URL. |
options |
Options |
Request, transport, redirect, retry, and cache options. |
The buffered response after redirects and retries complete.
type HttpVerb =
| "GET"
| "get"
| "HEAD"
| "head"
| "POST"
| "post"
| "PUT"
| "put"
| "DELETE"
| "delete"
| "CONNECT"
| "connect"
| "OPTIONS"
| "options"
| "TRACE"
| "trace"
| "PATCH"
| "patch"
| "PROPFIND"
| "propfind";Recognised HTTP methods. Input is case-insensitive and is normalised to uppercase before transport.
CONNECT is retained in the union for source compatibility with
sync-request, but request() rejects it because the buffered API cannot
expose the tunnel socket created by a successful CONNECT response.
type Options = {
proxy?: ProxyOptions;
rejectUnauthorized?: boolean;
caFile?: string;
localAddress?: string;
localInterface?: string;
tcpKeepAlive?: boolean
| {
idleSeconds?: number;
intervalSeconds?: number;
};
cacheNamespace?: string;
headers?: Headers;
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";
isMatch?: CacheIsMatchFunction;
isExpired?: CacheIsExpiredFunction;
canCache?: CacheCanCacheFunction;
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.
| Name | Type | Description |
|---|---|---|
proxy? |
ProxyOptions |
Explicit HTTP/HTTPS proxy origin URL. Ambient proxy variables are ignored. Defaults to no proxy. |
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. Defaults to false. |
cacheNamespace? |
string |
Private cache identity. Defaults to process.cwd(). |
headers? |
Headers |
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 |
Maximum time to wait for response headers in milliseconds. Defaults to 0, which disables it. |
overallTimeout? |
number |
Complete-operation deadline in milliseconds. Defaults to 0, which disables it. |
socketTimeout? |
number |
Socket inactivity timeout in milliseconds. Defaults to 0, which disables it. |
followRedirects? |
boolean |
Follow redirects automatically. Defaults to true. |
maxRedirects? |
number |
Maximum redirects to follow. Defaults to no limit. Negative values and infinities also mean no limit; NaN is invalid. |
allowRedirectHeaders? |
string[] |
Caller headers allowed to be forwarded to redirect hops. Defaults to none. |
gzip? |
boolean |
Transparently decompress gzip/deflate responses. Defaults to true. |
cache? |
"file" | "memory" |
Enable the private HTTP-aware cache in file or memory storage. Defaults to disabled. |
isMatch? |
CacheIsMatchFunction |
Override whether a stored cache variant matches the outgoing request. When caching is enabled, defaults to the built-in Vary comparison. |
isExpired? |
CacheIsExpiredFunction |
Override whether a matched cached response is expired. When caching is enabled, defaults to the built-in freshness calculation. |
canCache? |
CacheCanCacheFunction |
Override whether a completed origin response may be stored. When caching is enabled, defaults to the built-in response cacheability rules. |
agent? |
boolean | Agent |
sync-request boolean agent option, or a keep-alive Node Agent for connection reuse. Defaults to the standard connection behaviour without a dedicated persistent pool. |
retry? |
boolean | RetryFunction |
Retry GET requests, or provide a callback to decide per attempt. Defaults to disabled. |
retryDelay? |
number | RetryDelayFunction |
Retry delay in milliseconds, or a callback returning the delay. Defaults to 200 milliseconds when retries are enabled. |
maxRetries? |
number |
Maximum retry count. Defaults to 5 when retries are enabled. |
type Headers = IncomingHttpHeaders;HTTP header map used by then-request.
This is an alias of Node.js' IncomingHttpHeaders, exposed under
the historical then-request name so consumers do not need to import Node's
type directly.
type JsonPrimitive = string | number | boolean | null;Primitive JSON values accepted in request bodies.
type NestedJsonLike =
| JsonLike
| undefined
| {
toJSON: () => NestedJsonLike;
};Values accepted when nested inside JSON request bodies.
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.
An explicit HTTP/HTTPS proxy and optional Basic credentials.
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.
getBody(encoding): string;Read the response body as a string using the requested encoding.
| Parameter | Type |
|---|---|
encoding |
BufferEncoding |
string
getBody(): Buffer;Read the response body as a Buffer.
| Property | Type | Description |
|---|---|---|
statusCode |
number |
HTTP response status code. |
headers |
Headers |
Node-style response headers with lowercase keys. |
url |
string |
Final effective URL for the completed attempt. |
body |
Buffer |
Buffered response body. |
Buffered cached response passed to cache policy callbacks.
The body, headers, and request headers are defensive copies. Mutating them does not modify the stored cache entry.
| Property | Type | Description |
|---|---|---|
statusCode |
number |
Cached HTTP response status code. |
headers |
Headers |
Cached Node-style response headers with lowercase keys. |
body |
Buffer |
Buffered cached response body. |
requestHeaders |
Headers |
Request headers stored with this cache variant. |
requestTimestamp |
number |
Timestamp when the cached request started, in Unix milliseconds. |
Buffered origin response passed to canCache.
This is the normal public response shape for the completed GET request.
getBody(encoding): string;Read the response body as a string using the requested encoding.
| Parameter | Type |
|---|---|
encoding |
BufferEncoding |
string
getBody(): Buffer;Read the response body as a Buffer.
getJSON<T>(encoding?): T;Parse the buffered response body as JSON.
| Type Parameter | Default type |
|---|---|
T |
any |
| Parameter | Type |
|---|---|
encoding? |
BufferEncoding |
T
| Property | Type | Description | Inherited from |
|---|---|---|---|
statusCode |
number |
HTTP response status code. | RetryResponse.statusCode |
headers |
Headers |
Node-style response headers with lowercase keys. | RetryResponse.headers |
url |
string |
Final effective URL for the completed attempt. | RetryResponse.url |
body |
Buffer |
Buffered response body. | RetryResponse.body |
type CacheIsMatchFunction = (requestHeaders, cachedResponse, defaultValue) => boolean;Override whether a stored cache variant matches the outgoing request.
defaultValue is the built-in Vary comparison result.
| Parameter | Type |
|---|---|
requestHeaders |
Headers |
cachedResponse |
CachedResponse |
defaultValue |
boolean |
boolean
type CacheIsExpiredFunction = (cachedResponse, defaultValue) => boolean;Override whether a matched cached response is expired.
defaultValue is the result of the built-in freshness calculation.
| Parameter | Type |
|---|---|
cachedResponse |
CachedResponse |
defaultValue |
boolean |
boolean
type CacheCanCacheFunction = (response, defaultValue) => boolean;Override whether a completed origin response may be stored in the cache.
defaultValue is the built-in response cacheability result. Request-side
Cache-Control: no-store still disables storage before this callback runs.
| Parameter | Type |
|---|---|
response |
CachePolicyResponse |
defaultValue |
boolean |
boolean
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. Transport
failures are passed as CurlError instances; response parser failures are
passed as RequestError instances.
| Parameter | Type |
|---|---|
error |
CurlError | RequestError | null |
response |
RetryResponse | undefined |
attemptNumber |
number |
boolean
type RetryDelayFunction = (error, response, attemptNumber) => number;Return the delay in milliseconds before the next retry.
attemptNumber starts at 1 for the first completed attempt. Transport
failures are passed as CurlError instances; response parser failures are
passed as RequestError instances.
| Parameter | Type |
|---|---|
error |
CurlError | RequestError | null |
response |
RetryResponse | undefined |
attemptNumber |
number |
number
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.
<Encoding>(encoding): string;| Type Parameter |
|---|
Encoding extends BufferEncoding |
| Parameter | Type |
|---|---|
encoding |
Encoding |
string
(): Buffer;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 Parameter | Default type |
|---|---|
T |
any |
| Parameter | Type |
|---|---|
encoding? |
BufferEncoding |
T
Buffered synchronous response returned by request.
Helper methods observe later mutations to the public response object rather than a hidden immutable snapshot.
| Property | Type | Description |
|---|---|---|
isError |
() => boolean |
Return whether the response represents an HTTP error. |
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 |
Headers |
Node-style response headers with lowercase keys. |
url |
string |
Final effective URL after query handling and redirects. |
body |
Buffer<ArrayBufferLike> |
Mutable buffered response body. |
type BufferEncoding =
| "base64"
| "ascii"
| "utf8"
| "utf-8"
| "utf16le"
| "utf-16le"
| "ucs2"
| "ucs-2"
| "base64url"
| "latin1"
| "binary"
| "hex";Buffer encodings accepted by response body helpers.
One multipart entry accepted by FormData.
| Property | Type | Description |
|---|---|---|
contentType? |
string |
Optional media type override. |
knownLength? |
number |
Accepted for form-data append-option compatibility. |
header? |
string |
Optional raw multipart header that replaces generated part headers. |
key |
string |
Multipart field name. |
value |
string | number | boolean | Buffer<ArrayBufferLike> | Blob |
Synchronously materialisable multipart field value. |
fileName? |
string |
Optional file name. Path components are stripped before sending. |
Synchronous multipart/form-data builder compatible with the Node.js
FormData surface exposed by then-request.
Stream-valued parts and callback/stream methods from the form-data package
are intentionally omitted because this package is synchronous-only.
new FormData(): FormData;append(
key,
value,
options?
): void;Append a synchronously materialisable multipart field.
Numbers and booleans are converted to strings. The third argument may be a
filename string or the synchronous subset of form-data append options.
A custom header is serialized verbatim and replaces the generated
boundary and part headers, matching Node's form-data behavior. Local
path components are stripped from generated filenames before sending.
| Parameter | Type |
|---|---|
key |
string |
value |
string | number | boolean | Buffer<ArrayBufferLike> | Blob |
options? |
| string | { filename?: string; contentType?: string; knownLength?: number; header?: string; } |
void
getHeaders(): IncomingHttpHeaders & {
content-type: string;
};Return multipart request headers, merged with optional caller headers.
IncomingHttpHeaders & {
content-type: string;
}
getHeaders(userHeaders): Headers;Return multipart request headers, merged with optional caller headers.
| Parameter | Type |
|---|---|
userHeaders |
Headers |
getBoundary(): string;Return the boundary used to serialize this form.
string
setBoundary(boundary): void;Set the multipart boundary used by headers and serialization.
| Parameter | Type |
|---|---|
boundary |
string |
void
getBuffer(): Buffer;Serialize the complete multipart payload synchronously.
getLengthSync(): number;Return the exact byte length of getBuffer().
number
hasKnownLength(): boolean;All supported field values have a synchronously known length.
boolean
toString(): string;Match the identity string returned by Node's form-data package.
string
type RequestErrorCode = "ETIMEDOUT" | "ERR_TOO_MANY_REDIRECTS" | "ERR_REQUEST_FAILED";Stable transport-neutral error codes emitted by the TypeScript request layer.
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.
new CurlError(code, message): CurlError;| Parameter | Type |
|---|---|
code |
number |
message |
string |
Error.constructor| Property | Type | Description |
|---|---|---|
code |
number |
Numeric libcurl error code. |
Transport-neutral request failure created by the TypeScript request layer.
new RequestError(
code,
message,
options?
): RequestError;| Parameter | Type |
|---|---|
code |
RequestErrorCode |
message |
string |
options? |
ErrorOptions |
Error.constructor| Property | Modifier | Type | Description |
|---|---|---|---|
code |
readonly |
RequestErrorCode |
Stable transport-neutral request error code. |
HTTP status error thrown by response.getBody() for status codes >= 300.
The status, headers, body, and response URL that produced the error remain available on the error object.
new ResponseError(
statusCode,
headers,
body,
encoding?,
url?
): ResponseError;| Parameter | Type |
|---|---|
statusCode |
number |
headers |
Headers |
body |
Buffer |
encoding? |
BufferEncoding |
url? |
string |
Error.constructor| Property | Modifier | Type | Description |
|---|---|---|---|
statusCode |
readonly |
number |
HTTP status code that caused the error. |
headers |
readonly |
Headers |
Response headers returned by the server. |
body |
readonly |
Buffer |
Buffered response body returned by the server. |
url? |
readonly |
string |
Final response URL when the error came from Response#getBody(). |
Response#getJSON()is available as a convenience helper.cache: "memory"is available as an alternative to the file cache.isMatch,isExpired, andcanCacheexpose the synchronous cache-policy hooks fromhttp-basic. Callback/stream-based custom cache implementations remain out of scope; use the built-in"file"or"memory"cache.retryandretryDelaycan be callbacks when you need to decide retry behaviour at runtime. Transport failures passed to these callbacks areCurlErrorinstances with numeric libcurl error codes, while response parser failures areRequestErrorinstances rather than NodeErrnoExceptionerrors.agentstill accepts the boolean values supported bysync-request, and can also take a keep-alive NodeAgentfor connection reuse.overallTimeoutsets a deadline for the whole operation, alongside the response-headertimeoutand inactivitysocketTimeoutoptions.- TLS, local network binding, and TCP keepalive have dedicated options.
- RFC 9110 defines request
framing independently of the method, so request content is permitted on
GET,DELETE, andHEAD. 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,"", andnullare valid payloads. Response#getBody()throwsResponseErrorfor HTTP status codes >= 300. It still extendsErrorand exposesstatusCode,headers, andbody, but itsnameis"ResponseError"rather than sync-request's default"Error".- Invalid HTTP framing is rejected rather than sending conflicting
Content-LengthandTransfer-Encodingheaders. - Obsolete HTTP/1 response line folding is normalised to spaces as required for
user agents by RFC 9112.
sync-requestinherits Node's stricter parser, which can reject those responses instead. CONNECTis rejected explicitly. Althoughsync-requestaccepts it at the type level, its underlying buffered request stack does not complete a successful CONNECT tunnel response.- libcurl applies RFC 3986 URL normalisation, including removal of
.and..path segments.Response#urlreports libcurl's effective URL, so it can reflect that normalisation instead of preserving the caller's literal URL. - An explicit
Authorizationheader takes precedence over credentials in the URL, and a caller-suppliedAccept-Encodingheader is left unchanged. - 307 and 308 redirects preserve the request method and body.
sync-requestcan rewrite some body-bearing redirects toGET. - A redirect response without a
Locationheader is returned unchanged rather than being converted into an exception. RFC 9110's redirection semantics define automatic redirection in terms of a providedLocationvalue. This intentionally differs fromhttp-basic, which throws when a redirect status has no redirect target. - Query merging preserves additional literal
?and#delimiters thatsync-requestcan truncate while splitting URLs. - Default cache handling is stricter:
no-storetakes precedence,Ageis 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.
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.
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.
Prebuilt binaries are available for Apple Silicon (arm64) and Intel (x64) macOS.
Prebuilt binaries are available for x64 and arm64 Linux on both glibc and musl. GNU/Linux release binaries require GLIBC 2.31 or newer.
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.
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.