Skip to main content
Version: v8.0.0

Fetching

Fetching is the process of executing a request to communicate with a server. In Hyper-Fetch, this is primarily done using the send method on a request instance. This guide will walk you through the various ways to dispatch requests, pass data, and handle their lifecycle events.


What you'll learn
  1. How to fetch data from a server and handle responses.
  2. How to pass data, parameters, and query params to your requests.
  3. How to use lifecycle callbacks to monitor request states.
  4. How to abort a request.

Triggering a Request

The most direct way to execute a request is by calling the send method on it. This method is asynchronous and returns a promise that resolves with the request's outcome, including data, error, and status.

Promises never reject

One of Hyper-Fetch's design principles is that send() never rejects. Whether the request succeeds or fails, the promise always resolves with a response object containing data, error, success, and status. This eliminates the need for try...catch blocks around your requests.

Let's start by defining a request to fetch a user.

src/api/users.ts
import { client } from "client";

const getUser = client.createRequest<{ response: { id: number; name: string } }>()({
endpoint: "/users/:userId",
method: "GET",
});

Now, we can use it. The send method returns a rich object with details about the response.

import { getUser } from "src/api/users";

const requestWithParams = getUser.setParams({ userId: 1 });

// Execute the request and destructure the response
const { data, error, success, status } = await requestWithParams.send();

if (success) {
console.log(`Request successful with status ${status}!`);
console.log("User data:", data);
} else {
console.error(`Request failed with status ${status}.`, error);
}

Response Object

The send method resolves with an object containing the following properties:

ResponseType
Name Type Description
data
GenericDataType | null
Response data when successful, null on error
error
GenericErrorType | null
Error payload when the request fails, null on success
extra
ExtractAdapterExtraType<Adapter> | null
Adapter-specific metadata (e.g., response headers)
requestTimestamp
number
Timestamp (ms) when the request was sent
responseTimestamp
number
Timestamp (ms) when the response was received
status
ExtractAdapterStatusType<Adapter> | null
HTTP status code or adapter-specific status indicator
success
boolean
Whether the request completed successfully


Passing Data to Requests

Hyper-Fetch provides flexible ways to pass data, parameters, and query parameters to your requests.

1. Using Setter Methods

You can use methods like setPayload, setParams, and setQueryParams to prepare a request before sending it. This is useful for building up a request in multiple steps or based on dynamic conditions.

const updateUser = client.createRequest<{
response: { id: number; name: string };
payload: { name: string };
queryParams?: { limit?: number };
}>()({
endpoint: "/users/:userId",
method: "PUT",
});

const preparedRequest = updateUser.setParams({ userId: 1 }).setPayload({ name: "New Name" }).setQueryParams({
limit: 10,
});

const { data } = await preparedRequest.send();

2. Passing Data Directly to send

For a more direct approach, you can pass data, params, and query params as an object to the send method. This is often cleaner and more readable for simple requests.

// In a real app, you would import a pre-configured request
const postData = client.createRequest()({
endpoint: "/posts",
method: "POST",
});

async function createPost() {
const { data, error } = await postData.send({
payload: { title: "New Post", content: "..." },
queryParams: { authorId: 5 },
});

if (data) {
console.log("Post created:", data);
} else {
console.error("Failed to create post:", error);
}
}

createPost();

How to get requestId?

The requestId is a unique identifier for each request execution. It's particularly useful for debugging, logging, or tracking a request's lifecycle. Here are two ways to get it:

1. Using the Dispatcher

You can get the requestId by adding a request directly to the dispatcher. The client instance has two dispatchers: fetchDispatcher for GET requests and submitDispatcher for POST, PUT, PATCH, DELETE requests. The add method on a dispatcher returns the requestId.

import { getUser } from "src/api/users";

const requestWithParams = getUser.setParams({ userId: 1 });

// Add to the dispatcher and get the requestId
const requestId = client.fetchDispatcher.add(requestWithParams);

console.log("Request ID:", requestId);
Important

When you use dispatcher.add(), the request is executed immediately. You don't need to call .send() afterward. The dispatcher.add() method essentially is a send that gives you back the requestId synchronously.

2. Using Lifecycle Callbacks

The requestId is also available in the lifecycle callbacks of the send method. The onBeforeSent callback is the earliest point in the lifecycle where you can access it.

import { getUser } from "src/api/users";

const requestWithParams = getUser.setParams({ userId: 1 });

let requestId;

await requestWithParams.send({
onBeforeSent: (options) => {
requestId = options.requestId;
console.log("Request ID from callback:", requestId);
},
});

This approach is useful when you want to trigger the request with send and still need access to the requestId for tracking purposes within the same scope.


Cache Policy

The send method supports a cachePolicy option that controls how caching behaves for the request. This lets you fine-tune whether to use cached data or always go to the network:

// Always fetch from the network, ignoring any cached data
const { data } = await getUser.setParams({ userId: 1 }).send({
cachePolicy: "network-only",
});

// Return cached data if available and fresh, otherwise fetch from the network
const { data: cachedFirst } = await getUser.setParams({ userId: 1 }).send({
cachePolicy: "cache-first",
});

// Return cached data immediately but also revalidate in the background
const { data: revalidated } = await getUser.setParams({ userId: 1 }).send({
cachePolicy: "revalidate",
});

Aborting a Request

You can abort a request using the native AbortController. Simply create a controller and pass its signal to the send method. This is useful for canceling requests that are no longer needed, for example, when a user navigates away from a component.

Live results

Request Lifecycle Callbacks

The send method allows you to hook into the entire lifecycle of a request, from the moment it's about to be sent until it's finished. This is powerful for side-effects like showing loading indicators, logging, or performance monitoring.

All callbacks are optional and can be passed directly to the send method. Each callback receives the request instance and its requestId as arguments.

const { data, error } = await someRequest.send({
// Called right before the request is sent
onBeforeSent: ({ request, requestId }) => {
console.log(`Request ${requestId} is about to be sent.`);
},
// Called when the request starts
onRequestStart: ({ request, requestId }) => {
console.log(`Request ${requestId} has started.`);
// Ideal for showing a loading spinner
},
// Called when the adapter starts processing the request
onResponseStart: ({ request, requestId }) => {
console.log(`Adapter for ${requestId} has started.`);
},
// Called on upload progress
onUploadProgress: ({ progress, total, loaded }) => {
console.log(`Upload progress: ${progress}% (${loaded} / ${total} bytes)`);
},
// Called on download progress
onDownloadProgress: ({ progress, total, loaded }) => {
console.log(`Download progress: ${progress}% (${loaded} / ${total} bytes)`);
},
// Called upon receiving a response, whether success or error
onResponse: ({ response, request, requestId }) => {
console.log(`Request ${requestId} finished with status: ${response.status}`);
// You can check response.success to see if the request was successful
if (response.success) {
console.log(`Request ${requestId} succeeded with data:`, response.data);
} else {
console.error(`Request ${requestId} failed with error:`, response.error);
}
// Ideal for hiding a loading spinner
},
// Called when the request is removed from the dispatcher queue
onRemove: ({ request, requestId }) => {
console.log(`Request ${requestId} was removed from the queue.`);
},
});

Here is a live example demonstrating a few of these callbacks:

Live results

Congratulations!

You've learned the fundamentals of dispatching requests with Hyper-Fetch!

  • You can execute any request by calling its send method, which returns a rich response object.
  • You can pass data to a request using setter methods or directly within the send method's options.
  • You can abort a request using an AbortController's signal.
  • You can use lifecycle callbacks for handling side-effects during a request's journey.