-
Notifications
You must be signed in to change notification settings - Fork 1
ApiClient
🇺🇸 English: The core HTTP client of Bytekit. Typed, resilient, and isomorphic — works in Node.js 18+ and modern browsers. 🇪🇸 Español: El cliente HTTP central de Bytekit. Tipado, resiliente e isomórfico — funciona en Node.js 18+ y navegadores modernos.
ES: fetch nativo es poderoso pero crudo. Para cada petición necesitas: timeout, reintentos, circuit breaker, interceptores, manejo de errores localizados, serialización, query params… ApiClient hace todo eso con una sola configuración.
EN: Native fetch is powerful but raw. For every request you need: timeout, retries, circuit breaker, interceptors, localized error messages, serialization, query params… ApiClient handles all of that with a single config.
import { ApiClient, createApiClient, ApiError } from "bytekit/api-client";
// or from the barrel
import { ApiClient } from "bytekit";const api = new ApiClient({
baseUrl: "https://api.example.com/v1", // Required (or baseURL)
baseURL: "https://...", // Alias for baseUrl
defaultHeaders: { "X-App-Version": "2.0" },
fetchImpl: customFetch, // Custom fetch implementation
locale: "es", // "es" | "en" — default error language
timeoutMs: 15000, // Timeout per request in ms
logHeaders: false, // Log headers in debug output
logSensitiveData: false, // Allow raw payload logging (v3: off by default)
redactHeaderKeys: ["authorization", "cookie", "x-api-key"],
logger: myLogger, // Logger instance
retryPolicy: { // See RetryPolicy wiki
maxAttempts: 3,
initialDelayMs: 100,
maxDelayMs: 10000,
backoffMultiplier: 2,
shouldRetry: (error, attempt) => true
},
circuitBreaker: { // See CircuitBreaker wiki
failureThreshold: 5,
successThreshold: 2,
timeoutMs: 60000
},
interceptors: {
request: async (url, init) => [url, init],
response: async (response) => response
},
disableInterceptors: false,
errorMessages: {
es: { 404: "No encontrado" },
en: { 404: "Not found" }
}
});| Property | Type | Default | Description |
|---|---|---|---|
baseUrl |
string | URL |
— |
Required (or baseURL). Base URL for all requests. |
baseURL |
string | URL |
— | Alias for baseUrl. |
defaultHeaders |
HeadersInit |
{} |
Headers included in every request. |
fetchImpl |
typeof fetch |
globalThis.fetch |
Custom fetch implementation. |
locale |
"en" | "es" |
"es" |
Default language for error messages. |
timeoutMs |
number |
15000 |
Timeout per request (ms). |
interceptors |
ApiClientInterceptors |
— | Request/response interceptors. |
disableInterceptors |
boolean |
false |
Disable all interceptors globally. |
logHeaders |
boolean |
false |
Include headers in debug logs. |
logSensitiveData |
boolean |
false |
Allow raw request/response payload logging. Disabled by default since v3 to prevent secrets leaking into logs. Do not enable in production. |
redactHeaderKeys |
string[] |
["authorization", "cookie", ...] |
Header keys to redact in logs. |
logger |
Logger |
— | Logger instance. |
retryPolicy |
RetryConfig |
{ maxAttempts: 3 } |
Retry configuration. |
circuitBreaker |
CircuitBreakerConfig |
{ failureThreshold: 5 } |
Circuit breaker configuration. |
errorMessages |
Partial<Record<Locale, ...>> |
{} |
Custom error messages by status code. |
const user = await api.get<User>("/users/1");
// With query params
const users = await api.get<User[]>("/users", {
searchParams: { role: "admin", active: true }
});// Simple: body as second argument
const created = await api.post<User>("/users", { name: "Ana", role: "admin" });
// Advanced: with full options
const created = await api.post<User>("/users", {
body: { name: "Ana" },
headers: { "X-Idempotency-Key": "abc-123" },
timeoutMs: 5000
});const updated = await api.put<User>("/users/1", { name: "Ana Updated" });const patched = await api.patch<User>("/users/1", { role: "superadmin" });await api.delete<void>("/users/1");💡 Tip / Consejo: POST, PUT y PATCH aceptan un body plano O un objeto
RequestOptionscomo segundo argumento. ApiClient auto-detecta cuál estás pasando.
ES: getList convierte automáticamente parámetros de paginación, filtros y orden en query string params.
EN: getList automatically converts pagination, filter, and sort params into query string params.
interface ProductFilters {
category?: string;
minPrice?: number;
}
const result = await api.getList<Product, ProductFilters>("/products", {
pagination: { page: 1, limit: 20 },
sort: { field: "price", order: "desc" },
filters: { category: "electronics", minPrice: 100 }
});
// result.data → Product[]
// result.pagination → { page, limit, total, totalPages, hasNextPage, hasPreviousPage }| Property | Type | Description |
|---|---|---|
data |
T[] |
Array of items for the current page. |
pagination.page |
number |
Current page number. |
pagination.limit |
number |
Items per page. |
pagination.total |
number |
Total items across all pages. |
pagination.totalPages |
number |
Total number of pages. |
pagination.hasNextPage |
boolean |
Whether there's a next page. |
pagination.hasPreviousPage |
boolean |
Whether there's a previous page. |
await api.get<Data>("/endpoint", {
searchParams: { q: "bytekit" },
headers: { "Accept-Language": "en" },
timeoutMs: 5000,
errorLocale: "en",
validateResponse: zodAdapter(MySchema),
skipRetry: true,
skipInterceptors: true,
logHeaders: true
});| Property | Type | Description |
|---|---|---|
searchParams |
Record<string, QueryParam> |
Query string parameters. |
headers |
HeadersInit |
Override/add headers for this request. |
timeoutMs |
number |
Override timeout. |
errorLocale |
"en" | "es" |
Override error language. |
validateResponse |
ValidationSchema | SchemaAdapter<T> |
Validate response body. |
skipRetry |
boolean |
Skip retry policy. |
skipInterceptors |
boolean |
Skip interceptors. |
logHeaders |
boolean |
Log headers. |
logSensitiveData |
boolean |
Allow raw payload logging for this request. Overrides client-level setting. |
body |
object | FormData | string | Blob | ArrayBuffer |
Request body. |
import { zodAdapter } from "bytekit/schema-adapter";
import { z } from "zod";
const UserSchema = z.object({ id: z.number(), name: z.string() });
// Response is validated AND typed as { id: number; name: string }
const user = await api.get("/users/1", {
validateResponse: zodAdapter(UserSchema)
});See SchemaAdapter for Zod, Valibot, and custom adapters.
ES: Los interceptores modifican peticiones y respuestas globalmente. Útil para autenticación, refresh tokens, logging.
EN: Interceptors modify requests and responses globally. Useful for auth, token refresh, logging.
const api = new ApiClient({
baseUrl: "https://api.example.com",
interceptors: {
request: async (url, init) => {
const token = await getAccessToken();
const headers = { ...init.headers, Authorization: `Bearer ${token}` };
return [url, { ...init, headers }];
},
response: async (response) => {
if (response.status === 401) await refreshAccessToken();
return response;
}
}
});import { ApiError } from "bytekit/api-client";
try {
await api.get("/protected");
} catch (error) {
if (error instanceof ApiError) {
error.status; // 401
error.statusText; // "Unauthorized"
error.message; // "Necesitas iniciar sesión para continuar."
error.body; // Parsed response body (raw — read directly only in controlled code)
error.isTimeout; // false
error.details; // { status, statusText, message, body (sanitized), isTimeout }
}
}
⚠️ v3 — Safe serialization / Serialización segura por defectoEN: Since v3,
ApiError#toJSON(),ApiError#toString(), andApiError#detailssanitize the response body before exposing it. Sensitive fields (tokens, passwords, cookies, API keys, etc.) are redacted and long strings are truncated. This prevents secrets from leaking into logs and telemetry. If you need the raw body for debugging, readerror.bodydirectly in controlled code. Do not log or serializeerror.bodyin production.ES: Desde v3,
ApiError#toJSON(),ApiError#toString()yApiError#detailssanean el cuerpo de la respuesta antes de exponerlo. Los campos sensibles (tokens, contraseñas, cookies, API keys, etc.) se redactan y los strings largos se truncan. Esto evita que secretos lleguen a logs y telemetría. Si necesitás el body crudo para depurar, leelo directamente en código controlado enerror.body. No lo loguees ni lo serialices en producción.
| Status | Español | English |
|---|---|---|
| 400 | La solicitud es inválida. Verifica los datos enviados. | Invalid request. Please check your data. |
| 401 | Necesitas iniciar sesión para continuar. | You must be signed in to continue. |
| 403 | No tienes permisos para realizar esta acción. | You don't have permission for this action. |
| 404 | El recurso solicitado no fue encontrado. | Resource not found. |
| 408 | La solicitud tardó demasiado en responder. | Request timeout. |
| 429 | Demasiadas solicitudes. Intenta nuevamente más tarde. | Too many requests. Try again later. |
| 500 | Ocurrió un error interno en el servidor. | Internal server error. |
import { createApiClient } from "bytekit/api-client";
const api = createApiClient({ baseUrl: "https://api.example.com" });- RetryPolicy — Retry configuration.
- CircuitBreaker — Circuit breaker protection.
- SchemaAdapter — Zod / Valibot validation adapters.
- ResponseValidator — Built-in schema validation.
- Logger — Structured logging integration.