Request Hooks
Hooks are the request-level lifecycle: small functions that can read or mutate the request / response, and can interrupt the pipeline by returning false.
Lifecycle
There are 4 hooks: before, init, success, failed. They run in this order:
Handle Request
before– the instance exists, but the underlyingRequestobject hasn't been built yet. This is the right place to tweakrequest.config(headers, query, body, …).init– theRequestobject exists and is about to be passed tofetch. Tweaks torequest.confighere are too late, but you can still abort by returningfalse.
Handle Response
success– the response has been parsed and matchesglobalConfig.handler.responseCode(default: 2xx).failed– either the response failed (HTTP error / non-2xx), or the underlying fetch threw.
Hooks may return a Promise<boolean | undefined>; awaited by Fastjs.
Hook signatures
BeforeSend (before, init)
RequestHooks.BeforeSend receives its own request instance and returns boolean | undefined (or a Promise of that). Return false to abort.
RequestSuccess (success)
RequestHooks.RequestSuccess receives the full RequestReturn. Return false to convert success into failure (the .catch handlers will fire).
RequestFailed (failed)
RequestHooks.RequestFailed receives the error or HTTP status code, plus the request instance. error is either an Error (network error or interception) or a number (HTTP status code when the response code wasn't accepted).
Hook Type Declarations
namespace RequestHooks {
type BeforeSend = (
request: FastjsRequest,
) => boolean | undefined | Promise<boolean | undefined>;
type RequestSuccess = (
response: RequestReturn,
) => boolean | undefined | Promise<boolean | undefined>;
type RequestFailed = (
error: Error | number,
request: FastjsRequest,
) => boolean | undefined | Promise<boolean | undefined>;
}RequestHookParam Type Declaration
interface RequestHookParam {
before?: RequestHooks.BeforeSend[] | RequestHooks.BeforeSend;
init?: RequestHooks.BeforeSend[] | RequestHooks.BeforeSend;
success?: RequestHooks.RequestSuccess[] | RequestHooks.RequestSuccess;
failed?: RequestHooks.RequestFailed[] | RequestHooks.RequestFailed;
/** Keep running the remaining hooks even after one returns false. */
runAll?: boolean;
}Returning false
Any hook may return false to interrupt the request. The next callback in the failure chain will receive a FailedParams with:
intercept: truehook: "<hook name>"error: new Error("Request interrupted by <hook>")
request.config.hooks.before = (req) => {
if (!isLoggedIn()) return false; // stops the request, triggers .catch
};Set hooks
Hooks can be set at global or per-instance level.
Global hooks
Global hooks affect every request that goes through request.*.
request.config.hooks.before = (request) => {
request.config.headers.Authorization = `Bearer ${getToken()}`;
};Instance hooks
Instance hooks are scoped to one request instance:
let loading = false;
const getUser = request
.create("https://reqres.in/api/users", null, {
hooks: {
before: () => {
if (loading) return false;
loading = true;
},
},
})
.finally(() => {
loading = false;
});Multi hooks v1.0.0-alpha.15
Order
When both instance hooks and global hooks are set, the instance hooks run first; if they don't abort, the global hooks run next.
Pass an array to register multiple hooks of the same type. By default the chain stops at the first hook that returns false:
request.config.hooks.before = [
() => {
console.log("First");
return false;
},
() => {
console.log("Second"); // NOT called
},
];
request.get("https://reqres.in/api/users");
// Log: FirstSet hooks.runAll = true if you want every hook to execute even after one returns false:
request.config.hooks.runAll = true;
request.config.hooks.before = [
() => {
console.log("First");
return false;
},
() => {
console.log("Second"); // also called
return false;
},
];
request.get("https://reqres.in/api/users");
// Log: First
// Log: SecondArray hooks also work in per-call config:
request.get("https://reqres.in/api/users", null, {
hooks: {
before: [() => {}, () => {}],
},
});Examples
Inject a custom referer header
request.config.hooks.before = (request) => {
request.config.headers["x-refer"] = location.href;
};Tap into successful responses
request.config.hooks.success = [
() => console.log("Request success"),
(response) => console.log("Response data:", response.data),
];Refresh an access token transparently
let refreshing: Promise<void> | null = null;
request.config.hooks.failed = async (err, req) => {
if (err === 401 && !refreshing) {
refreshing = refreshToken().finally(() => (refreshing = null));
await refreshing;
req.resend?.(); // re-run the original call
return false; // suppress the original failure
}
};Type reference
RequestReturn
interface RequestReturn {
headers: FastjsHeaders; // native Headers + toArray() / toObject()
response: Response; // native Response
data: RequestReturnData; // parsed body, same as .then's first arg
status: number;
request: FastjsRequest;
resend: () => FastjsRequest;
}FailedParams
interface FailedParams<T extends Error | number> {
error: T;
request: FastjsRequest;
intercept: boolean;
hook: "before" | "init" | "success" | "failed" | null;
response: RequestReturn | null;
headers: FastjsHeaders | null;
resend: () => FastjsRequest;
}