Client
Client
is the central class for configuring your server connection in Hyper Fetch. It initializes all core
subsystems—such as queues, cache, and interceptors—and provides a unified way to create and manage requests based on its
configuration. By keeping all data and logic encapsulated within a single client instance, you ensure that each client
remains isolated and independent from others.
- Builder for all of the requests
- Central place for all the default settings
- Handles authentication and interceptors
- Place where all sub-modules are initialized
Initialization
The client is initialized with the createClient
function.
import { createClient } from "@hyper-fetch/core";
export const client = createClient({ url: "http://localhost:3000" });
Available methods
Name | Description |
---|---|
| |
| |
| |
| Set the new logger instance to the Client |
| Set the logger severity of the messages displayed to the console |
| Set globally if mocking should be enabled or disabled for all client requests. |
| This method enables the logger usage and display the logs in console |
| |
| Set custom http adapter to handle graphql, rest, firebase or others |
| Key setters |
| Remove plugins from Client |
| Method for removing listeners on success. |
| Method for removing listeners on request. |
| Method for removing listeners on request. |
| Method for removing listeners on error. |
| Method for removing listeners on auth. |
| Method for intercepting success responses. |
| Method for intercepting any responses. |
| Method of manipulating requests before sending the request. |
| Method for intercepting error responses. It can be used for example to refresh tokens. |
| Method of manipulating requests before sending the request. We can for example add custom header with token to the request which request had the auth set to true. |
| Hydrate your SSR cache data |
| Create requests based on the Client setup |
| Clears the Client instance and remove all listeners on it's dependencies |
| Add persistent plugins which trigger on the request lifecycle |
Features
Here are some of the most important features available in the Client class.
The Client
is designed to be used as a builder of requests, serving as the global entry point for server communication
throughout your application. This approach prevents duplicated logic, promotes a consistent architecture, and makes it
easier to maintain and test your code by centralizing configuration and types.
-
createRequest()
The createRequest
method is the main method for creating templates for the API connectivity. We use the builder
pattern to create a request object which contain crucial information about the request - endpoint, method, etc.
Each request keep the reference to the client, so it can access the global settings.
import { createClient } from "@hyper-fetch/core";
const client = createClient({ url: "http://localhost:3000" });
const getUser = client.createRequest<{ response: User }>()({ endpoint: "/users/1" });
console.log(getUser);
-
Authentication
To send authenticated requests, set up the onAuth
interceptor. Set up the request with the auth
option set to
true.
import { createClient } from "@hyper-fetch/core";
import { store } from "./store"; // Your state management store
export const client = createClient({ url: "http://localhost:3000" }).onAuth((request) => {
// Get the auth token from your store
const state = store.getState();
const authToken = state.auth.token;
// Add the token to the request headers
return request.setHeaders({
...request.headers,
Authorization: `Bearer ${authToken}`,
});
});
// Create an authenticated request
export const getUsers = client.createRequest()({
endpoint: "/users",
auth: true, // This enables the onAuth interceptor
});
-
Interceptors
Interceptors are a powerful feature that allows you to modify requests before they're sent or responses before they are processed within other core modules.
They can modify the request before it's sent or the response after it's received, depending on the type of interceptor.
Request Interceptor
onRequest()
- allows to modify the request before it's sent giving ability to modify its dataonAuth()
- allows to modify the request withauth: true
before it's sent, giving ability to pass custom auth headers or other any options
Response Interceptor
There are several methods for intercepting a response from a request:
onResponse()
- intercepts any response after it's received to modify it's bodyonError()
- intercepts the error response after it's received to modify it's bodyonSuccess()
- intercepts the success response after it's received to modify it's body
We can modify received data with these interceptors before it will be emitted to other sub-systems.
import { createClient } from "@hyper-fetch/core";
export const client = createClient({ url: "http://localhost:3000" })
// Add request interceptor
.onRequest((request) => {
// Add custom headers to all requests
return request.setHeaders({
...request.headers,
"x-custom-header": "custom-value",
});
})
// Add response interceptor
.onResponse(async (response, request) => {
// Log all responses
console.log(`Response for ${request.endpoint}:`, response);
return response;
})
// Add error interceptor
.onError(async (errorResponse, request) => {
// Handle specific error responses
if (errorResponse.status === 401 && !request.used) {
// Implement token refresh logic
// Set Used allows you to prevent infinite loops of retries
return request.setUsed(true).send();
}
return errorResponse;
});
-
Global settings
Every module is initialized inside of the Client
class. This is a perfect place to store and control global settings
for the adapter, cache, requests or other modules.
import { createClient } from "@hyper-fetch/core";
export const client = createClient({
url: "http://localhost:3000",
});
client.adapter.setRequestDefaults({
retries: 3, // Retry failed requests 3 times
cacheTime: 5 * 60 * 1000, // Cache data for 5 minutes
staleTime: 60 * 1000, // Data becomes stale after 1 minute
// Adapter options
options: {
timeout: 10000, // 10 seconds timeout for all requests
},
});
// These global settings can be overridden for specific requests
export const getUser = client.createRequest()({
endpoint: "/users/:id",
method: "GET",
timeout: 5000, // Override the global timeout of 10000 for this request
});
-
Plugins
Plugins are a powerful feature that allows you to extend the functionality of the client. They can be used to listen to the events happening inside of the Hyper Fetch.
import { createClient } from "@hyper-fetch/core";
import { Plugin } from "@hyper-fetch/core";
import { DevtoolsPlugin } from "@hyper-fetch/plugin-devtools";
// Add the official devtools plugin
export const client = createClient({ url: "http://localhost:3000" })
.setDebug(true)
.addPlugin(
DevtoolsPlugin({
appName: "My Application",
}),
);
// Create a custom plugin
const loggingPlugin = new Plugin({
name: "logging-plugin",
})
.onRequestStart(({ request }) => {
console.log(`Request started: ${request.method} ${request.endpoint}`);
})
.onRequestSuccess(({ response, request }) => {
console.log(`Request succeeded: ${request.method} ${request.endpoint}`, response.data);
})
.onRequestError(({ response, request }) => {
console.error(`Request failed: ${request.method} ${request.endpoint}`, response.error);
});
// Add the custom plugin to the client
client.addPlugin(loggingPlugin);
-
Hydration
We can hydrate the client with the data from the server. This is a powerful feature that allows us to pass the data from the server to the client for the SSR use cases.
// Server-side code
import { createClient } from "@hyper-fetch/core";
export const client = createClient({ url: "http://localhost:3000" });
const userRequest = client.createRequest()({ endpoint: "/users/1" });
// Fetch data on the server
const response = await userRequest.send();
// Dehydrate the response for client hydration
const dehydratedData = userRequest.dehydrate({ response });
// Pass dehydratedData to the client (e.g., as props or through a global state)
// *****************************************************
// *****************************************************
// Client-side code
// *****************************************************
// *****************************************************
import { createClient } from "@hyper-fetch/core";
import { useFetch } from "@hyper-fetch/react";
export const client = createClient({ url: "http://localhost:3000" });
// Hydrate the client with server data
client.hydrate([dehydratedData]);
// In your React component
function UserProfile() {
// Data will be available immediately from cache, no loading state needed
const { data } = useFetch(client.createRequest()({ endpoint: "/users/1" }), {
revalidate: false, // Prevent automatic refetching
});
return <div>{data?.name}</div>;
}
TypeScript
Most important type passed to the createClient
is the error type. It is a global error type, being used like a default
for any request errors. It is later combined with the "local error" type of the requests creating the union. This way we
handle generic errors from the server (global) as well as some validation/business (local) errors for given endpoint.
It is important to use the eslint plugin to get the best typescript experience. We enhance the abilities of the object generics to provide you with the best possible DX.
import { createClient, AdapterType } from "@hyper-fetch/core";
// Define your global error type
interface ClientErrorType {
message: string;
code: number;
}
const client = createClient<{ error: ClientErrorType }>();
-
error
defines the global/default error type used in all the requests. It should consist of anError
type and your defaultServerErrorType
. It is propagated down to thecreateRequest
method, serving as a default for theglobalError
type. -
adapter
is the generic responsible for shaping options passed to the adapter. Most likely you will change it only when you provide your custom adapter.
Modules
Client is a place where all the subsystems are initialized. Here is a diagram of the client and its subsystems: