Skip to content

Scaling up with request ​

The previous page covered the bread-and-butter request.get / request.post. This page is for everything else: headers, timeouts, debouncing, cancellation, custom response handling, and the request.config global.

Set request headers ​

Pass headers in the third (config) argument.

typescript
request.get("https://reqres.in/api/users", null, {
  headers: {
    Authorization: "Bearer token",
  },
});

Common Question ​

Q: I'm trying to send a Cookie header from JavaScript and it doesn't work.

A: Cookie, Authorization (in some browsers), Set-Cookie, Host and the rest of the forbidden header names are off-limits to scripts. The browser silently strips them. Use the cookie module to write a cookie that the browser will then send automatically.

Set request timeout ​

config.timeout (in milliseconds) aborts the request via AbortSignal.timeout. 0 disables it.

typescript
request.get("https://looooong.example/", null, { timeout: 10_000 });

Very short timeouts

In development mode, a fastjs/request warning is logged when timeout is ≤ 1000 ms – it is easy to accidentally abort every request that way.

Set default timeout ​

request.config is the global config object. Anything you set on it becomes the default for subsequent calls.

typescript
request.config.timeout = 10_000;

Built-in debounce ​

config.wait (milliseconds) collapses multiple calls into one – the request is only fired after wait has passed without another call. Calling abort() while waiting cancels the pending timer.

typescript
import { dom, request } from "jsfast";

dom.newEl("button", { text: "Click me" }).addEvent("click", () => {
  request
    .get("https://reqres.in/api/users", null, { wait: 1000 })
    .then((data) => console.log(data));
});

Cancelling a request ​

Every FastjsRequest exposes abort(reason?). It clears any pending debounce timer and aborts the underlying fetch via its AbortController.

typescript
const req = request.get("https://reqres.in/api/long");
req.then((data) => console.log(data));

// Later, e.g. when the user navigates away:
req.abort("navigated away");

abort() before the request has been dispatched (e.g. during wait) is a safe no-op for downstream observers.

Customise the response handling ​

request.config.handler controls how Fastjs decides "success vs failure" and how the body is decoded.

By default Fastjs decodes the body as JSON when Content-Type includes application/json, otherwise as text, and treats any 2xx code as success.

Example: accept any 4xx as "success" so you can read the body in .then:

typescript
request.config.handler.responseCode = (code) => code >= 200 && code < 500;

Example: always decode as ArrayBuffer:

typescript
request.config.handler.handleResponse = async (response) =>
  await response.arrayBuffer();

GlobalConfig Type Declaration ​

typescript
interface GlobalConfig {
  timeout: number;
  hooks: RequestHookParam;
  handler: {
    handleResponse: (
      response: Response,
      request: FastjsRequest,
    ) => Promise<any>;
    responseCode: (code: number, request: FastjsRequest) => boolean;
  };
  check: {
    ignoreFormatWarning: boolean;
    stringBodyWarning: boolean;
    unrecommendedMethodWarning: boolean;
  };
}

The default implementation:

typescript
handler: {
  // application/json → json(); everything else → text()
  handleResponse: async (response) => {
    if (response.headers.get("Content-Type")?.includes("application/json")) {
      return await response.json();
    }
    return await response.text();
  },
  // 2xx is "success"
  responseCode: (code) => code >= 200 && code < 300,
}

Per-call config reference ​

Anything you don't pass to config is filled in from request.config (the global). See Add Hooks for the hook fields.

RequestConfig Type Declaration ​

typescript
interface RequestConfig {
  timeout: number;
  headers: { [key: string]: string };
  wait: number;
  failed: (error: FailedParams<Error | number>) => void;
  callback: (data: RequestReturnData, response: RequestReturn) => void;
  query: { [key: string]: any } | string | null;
  body: { [key: string]: any } | string | null;
  hooks: RequestHookParam;
}

Reusing config ​

Use request.request(config) to materialise a RequestConfig (with global defaults merged in) and pass it around explicitly:

typescript
const baseCfg = request.request({
  timeout: 8000,
  headers: { "X-App": "demo" },
});

request.get("/api/posts", null, baseCfg);
request.post("/api/posts", { title: "Hi" }, baseCfg);

If you want a fully-fledged reusable instance with its own URL / data / hooks, use request.create instead.