Managers
Managers are core subsystems in Hyper Fetch that handle essential app-wide concerns—like network status, request lifecycle, and logging. They provide powerful, centralized APIs for managing state, events, and behaviors across your application, making advanced features like offline support, cancellation, and debugging easy to implement and maintain in other environments like React, Node.js, etc.
The library contains several managers, each supporting a specific subsystem or feature. Understanding these managers helps you unlock advanced capabilities and build robust, maintainable applications.
AppManager
The AppManager detects and manages your application's online/offline state, tracks window focus and blur events, and allows integration with custom event sources (such as React Native or Electron). It provides unmount callbacks for easy cleanup, making it simple to handle app-wide lifecycle events in any environment.
- Track online/offline status of the app
- Detect window focus and blur events
- Allow custom event sources for different environments
const unmountFocusListener = client.appManager.events.onFocus(() => {
console.log("App is focused!");
});
const unmountOfflineListener = client.appManager.events.onOffline(() => {
console.log("App is offline!");
});
// ... later, to clean up:
unmountFocusListener();
unmountOfflineListener();
Hyper Fetch event listeners always return unmounting callbacks for easier cleanup handling.
Customizing Events
If you're using a non-browser environment (like React Native), you can set custom event handlers:
client.appManager.setFocusEvents({
onFocus: myCustomFocusHandler,
onBlur: myCustomBlurHandler,
});
Events
| Name | Type | Description |
|---|---|---|
| emitBlur | | Emit when the application window loses focus |
| emitFocus | | Emit when the application window gains focus |
| emitOffline | | Emit when the application transitions to offline state |
| emitOnline | | Emit when the application transitions to online state |
| onBlur | | Listen for blur events |
| onFocus | | Listen for focus events |
| onOffline | | Listen for offline state transitions |
| onOnline | | Listen for online state transitions |
RequestManager
The RequestManager manages cancellation tokens for requests, emits events throughout the request lifecycle (including start, upload, download, response, and abort), and supports aborting requests by requestId or custom abortKey. It enables granular progress tracking and advanced cancellation scenarios for robust request handling.
- Manage cancellation tokens for requests
- Emit events for request lifecycle (start, upload, download, response, abort)
- Support aborting by requestId or abortKey
Bulk Abort
The abortAll() method aborts every in-flight request tracked by the RequestManager. This is useful for cleanup
on unmount, page navigation, or test teardown.
// Abort all tracked requests at once
client.requestManager.abortAll();
const unmountUploadListener = client.requestManager.events.onUploadProgressById(requestId, (progress) => {
console.log("Upload progress:", progress);
});
const unmountDownloadListener = client.requestManager.events.onDownloadProgressById(requestId, (progress) => {
console.log("Download progress:", progress);
});
// ... later, to clean up:
unmountUploadListener();
unmountDownloadListener();
You can abort requests by their requestId or by a custom abortKey to cancel groups of requests at once.
AbortKey
Every request added to the dispatcher creates an abort controller, stored in a Map under its abort key (usually the
requestId). This lets you abort single requests or groups of requests easily.
const getUser = client.createRequest()({
method: "GET",
endpoint: "/users/:userId",
});
const abortKey = getUser.abortKey; // "/users/:userId"
const abortKeyWithParams = getUser.setParams({ userId: 1 }).abortKey; // "/users/1"
const abortKeyWithQueryParams = getUser.setQueryParams({ page: 1 }).abortKey; // "/users/:userId"
Custom abortKey
You can also set a custom abort key:
import { client } from "./api";
const getUser = client.createRequest()({
method: "GET",
endpoint: "/users/:userId",
abortKey: "CUSTOM_ABORT_KEY",
});
console.log(getUser.abortKey); // "CUSTOM_ABORT_KEY"
Generic abortKey
You can also set a generic abort key:
import { client } from "./api";
client.setQueryKeyMapper((request) => {
if (request.requestOptions.endpoint === "/users/:userId") {
return `CUSTOM_ABORT_KEY_${request.params?.userId || "unknown"}`;
}
});
const getUser = client.createRequest()({
method: "GET",
endpoint: "/users/:userId",
});
console.log(getUser.setParams({ userId: 1 }).queryKey); // "CUSTOM_QUERY_KEY_1"
Events
| Name | Type | Description |
|---|---|---|
| emitAbort | | Emit when a request is aborted |
| emitDeduplicated | | Emit when a request is deduplicated (skipped because an identical one is in-flight) |
| emitDownloadProgress | | Emit download progress updates |
| emitLoading | | Emit loading state changes for a request |
| emitRemove | | Emit when a request is removed from the dispatcher queue |
| emitRequestStart | | Emit when a request starts being sent |
| emitResponse | | Emit when a response is received |
| emitResponseStart | | Emit when the response starts being received |
| emitUploadProgress | | Emit upload progress updates |
| onAbort | | |
| onAbortById | | |
| onAbortByKey | | |
| onDeduplicated | | Listen for deduplicated request events |
| onDeduplicatedByCache | | |
| onDeduplicatedById | | |
| onDeduplicatedByQueue | | |
| onDownloadProgress | | |
| onDownloadProgressById | | |
| onDownloadProgressByQueue | | |
| onLoading | | |
| onLoadingByCache | | |
| onLoadingById | | |
| onLoadingByQueue | | |
| onRemove | | |
| onRemoveById | | |
| onRemoveByQueue | | |
| onRequestStart | | |
| onRequestStartById | | |
| onRequestStartByQueue | | |
| onResponse | | |
| onResponseByCache | | |
| onResponseById | | |
| onResponseStart | | |
| onResponseStartById | | |
| onResponseStartByQueue | | |
| onUploadProgress | | |
| onUploadProgressById | | |
| onUploadProgressByQueue | |
client.requestManager.events.onResponse((response, details) => {
// Handle response
});
LoggerManager
The LoggerManager centralizes logging for all subsystems, enables creation of custom loggers for different modules or features, and ensures consistent logging by inheriting parent configuration. It offers flexible log levels and output formatting to keep logs organized and actionable.
- Centralize logging for all subsystems
- Enable creation of custom loggers for modules/features
- Inherit parent configuration for consistent logging
client.setDebug(true) must be called for any log output to appear. Without it, the LoggerManager silently discards
all messages regardless of log level.
const client = createClient({ url: "http://localhost:3000" }).setDebug(true);
const logger = client.loggerManager.init("My Module");
logger.error("Something went wrong!"); // Output: [My Module] Something went wrong!
Filtering Logs by Module
Use setModules to restrict log output to specific module names. When set, only loggers whose names match the provided
list will produce output. Pass undefined to remove the filter and show all modules.
// Only show logs from "Cache" and "Dispatcher" modules
client.loggerManager.setModules(["Cache", "Dispatcher"]);
// Show all modules again
client.loggerManager.setModules(undefined);
- Always clean up event listeners using the returned unmount callbacks to avoid memory leaks.
- Use custom abort keys for advanced cancellation scenarios (e.g., aborting all requests for a specific feature).
- Leverage custom loggers to keep logs organized and actionable.
