Skip to main content

HTTP and API Client

HttpClient

The low-level HTTP client built on Axios. It handles:

  • automatic token injection
  • token refresh on 401 responses
  • business error code detection
  • file upload and download
  • path parameter replacement

Creating an HttpClient

import { createHttpClient } from "@vef-framework-react/core";

const http = createHttpClient({
baseUrl: "/api",
timeout: 30_000,
okCode: 0,
tokenExpiredCode: 1002,
getAuthTokens: () => tokenStore.getTokens(),
setAuthTokens: tokens => tokenStore.setTokens(tokens),
refreshToken: async tokens => {
const result = await http.post("/auth/refresh", { data: tokens });
return result.data;
},
onUnauthenticated: () => router.navigate({ to: "/login" }),
showErrorMessage: message => notification.error(message)
});

HttpClientOptions

OptionTypeDescription
baseUrlstringBase URL for all requests
timeoutnumberRequest timeout in ms
okCodeMaybeArray<number>Business success code(s)
tokenExpiredCodeMaybeArray<number>Token-expired code(s)
getAuthTokens() => Awaitable<AuthTokens | undefined>Retrieve current tokens
setAuthTokens(tokens) => Awaitable<void>Persist refreshed tokens
refreshToken(tokens) => Awaitable<AuthTokens>Refresh token callback
onUnauthenticated() => Awaitable<void>Called when auth fails
onAccessDenied() => Awaitable<void>Called on 403
showInfoMessage(msg) => voidInfo message handler
showWarningMessage(msg) => voidWarning message handler
showErrorMessage(msg) => voidError message handler

HTTP Methods

// GET
const result = await http.get<UserInfo>("/user/info", { params: { id: 1 } });

// POST
const result = await http.post<CreateResult>("/user/create", { data: payload });

// PUT
const result = await http.put<void>("/user/update", { data: payload });

// DELETE
const result = await http.delete<void>("/user/delete", { params: { id: 1 } });

// Upload
const result = await http.upload<UploadResult>("/file/upload", {
data: formData,
onProgress: event => console.log(event.loaded / event.total)
});

// Download
await http.download("/file/export", {
params: { id: 1 },
filename: "export.xlsx"
});

ApiResult<T>

All HTTP methods return ApiResult<T>:

interface ApiResult<T> {
readonly code: number;
readonly message: string;
readonly data: T;
}

Skipping Authentication

For requests that should not carry the Authorization header:

import { skipAuthenticationHeader, skipAuthenticationValue } from "@vef-framework-react/core";

await http.post("/auth/login", {
data: credentials,
headers: {
[skipAuthenticationHeader]: skipAuthenticationValue
}
});

isBusinessError

import { isBusinessError } from "@vef-framework-react/core";

try {
await http.post("/user/create", { data: payload });
} catch (error) {
if (isBusinessError(error)) {
// error.code, error.message are available
}
}

ApiClient

ApiClient combines HttpClient and QueryClient into a single object. In most projects, it is created once through starter.createApiClient() and shared across the application.

createApiClient (core)

The core-level factory. In application code, prefer starter.createApiClient() which adds token storage, message feedback, and unauthenticated handling on top.

import { createApiClient } from "@vef-framework-react/core";

const apiClient = createApiClient({
http: {
baseUrl: "/api",
okCode: 0
},
query: {
staleTime: 5_000,
gcTime: 300_000
}
});

ApiClientOptions

OptionTypeDescription
httpHttpClientOptionsOptions passed to HttpClient
queryQueryClientOptionsOptions passed to QueryClient

createQueryFn

Creates a typed query function with automatic abort signal injection.

export const findUserPage = apiClient.createQueryFn(
"find_user_page",
http => async (params, pageParam, meta) => {
const result = await http.post("/user/page", { data: params });
return result.data;
}
);

The returned function has a .key property for use in queryKey arrays:

useQuery({
queryKey: [findUserPage.key, searchParams],
queryFn: findUserPage
});

createMutationFn

Creates a typed mutation function.

export const createUser = apiClient.createMutationFn(
"create_user",
http => params => http.post("/user/create", { data: params })
);

fetchQuery and prefetchQuery

For imperative data fetching outside React components:

const userInfo = await apiClient.fetchQuery({
queryKey: [getUserInfo.key, { id: 1 }],
queryFn: getUserInfo
});

await apiClient.prefetchQuery({
queryKey: [getUserInfo.key, { id: 1 }],
queryFn: getUserInfo
});

executeMutation

For imperative mutations outside React components:

await apiClient.executeMutation({
mutationFn: login,
params: { username, password }
});

QueryKey<TParams>

The typed query key format used throughout the framework:

type QueryKey<TParams = never> = readonly [Key, ...If<IsNever<TParams>, [], [TParams]>];

MutationFunction<TData, TParams>

Extends the TanStack mutation function with a .key property for matching in useHasMutating.

QueryFunction<TData, TParams, TPageParam>

Extends the TanStack query function with a .key property for matching in useHasFetching.