@cactus-agents/api-client
HTTP client framework-agnostic baseado em fetch. Gerencia headers de tenant, idioma, autorização e callbacks de erro.
Instalação
pnpm add @cactus-agents/api-client
Uso básico
import { createApiClient } from '@cactus-agents/api-client';
const client = createApiClient({
baseUrl: 'https://api.example.com/v2',
tenant: 'my-tenant',
language: 'pt-br',
});
const response = await client.get<MyData>('/endpoint');
// response.data, response.status, response.headers
ApiClientConfig
interface ApiClientConfig {
baseUrl: string; // URL base da API
tenant: string; // Header tenant
language: string; // Idioma fallback
// Opcionais
buildId?: string; // ID do build
debug?: boolean; // Habilita header DEBUG
// Callbacks de dados
getAccessToken?: () => string | null;
getLocale?: () => string;
getOriginAccess?: () => OriginAccess;
getReferrerInfo?: () => ReferrerInfo | null;
getExtraHeaders?: () => Record<string, string>;
// Hooks de erro
onUnauthorized?: (url: string) => void; // 401
onChallenge?: () => void; // 429
onTimeoutLimit?: (url: string) => void; // 202
}
Métodos
| Método | Descrição |
|---|---|
get<T>(path) | GET com headers internos |
post<T>(path, payload?) | POST com headers internos |
put<T>(path, payload?) | PUT com headers internos |
patch<T>(path, payload?) | PATCH com headers internos |
delete<T>(path, payload?) | DELETE com headers internos |
getExternal<T>(url) | GET para URLs externas (sem headers internos) |
Headers automáticos
Em toda request interna (external !== true), o client adiciona:
| Header | Conteúdo |
|---|---|
tenant, origin-domain | config.tenant |
lang / language | getLocale() ou config.language |
version | vz3b-{buildId} (ou vm se sem buildId) |
X-LOG-INFO | 1-{Date.now()}-{buildId} — formato textual estável usado por tracing server-side |
Authorization: Bearer {token} | se getAccessToken() retorna token |
X-ORIGIN-ACCESS | se getOriginAccess() retorna valor ≠ Unknown |
X-ORIGIN-REFERRER | getReferrerInfo()?.referrer (URL do referrer first-touch) |
X-ORIGIN-HOSTNAME | getReferrerInfo()?.hostname |
X-ORIGIN-CINFO-ID | getReferrerInfo()?.cinfoId (vindo de X-INFOS-ID que o BFF retorna em login/register) |
X-ORIGIN-CINFO-ID-REF | getReferrerInfo()?.cinfoIdRef (vindo de X-INFOS-REF) |
O bloco
X-ORIGIN-*faz parte do tracking de marketing/atribuição — ver Marketing Tracking pra fluxo end-to-end de captura, política de atribuição e payloads associados.
Tratamento de status
| Status | Comportamento |
|---|---|
| 200-299 | Resolve com ApiResponse<T> |
| 202 | Resolve (não throw) + chama onTimeoutLimit |
| 401 | Chama onUnauthorized + throw |
| 429 | Chama onChallenge + throw |
| Outros non-ok | Throw |
ApiResponse
interface ApiResponse<T> {
data: T;
status: number;
statusText: string;
headers: Headers;
url?: string;
rawBody?: string;
}
OriginAccess
enum OriginAccess {
Unknown = 0,
App = 1,
Desktop = 2,
DesktopApp = 3,
Mobile = 4,
MobileApp = 5,
}
Helpers
O pacote exporta helpers de conveniência para quem consome o ApiClient junto com outros pacotes do SDK.
createFetcherFromClient(client)
Converte um ApiClient (ou qualquer objeto com get/post que retorna { data }) no formato de fetcher plano esperado por @cactus-agents/auth e @cactus-agents/brand:
import { createApiClient, createFetcherFromClient } from '@cactus-agents/api-client';
import { createAuthService } from '@cactus-agents/auth';
const client = createApiClient({ baseUrl, tenant, language });
const fetcher = createFetcherFromClient(client);
const auth = createAuthService(fetcher);
Na maioria dos casos, prefira usar
createAuthFromClientoucreateBrandFromClientdiretamente — eles fazem essa conversão internamente.
createFullFetcherFromClient(client)
Versão estendida do createFetcherFromClient que expõe todos os 5 métodos HTTP (get, post, put, patch, delete). Usado por pacotes que precisam de mais do que get/post, como @cactus-agents/user:
import { createApiClient, createFullFetcherFromClient } from '@cactus-agents/api-client';
import { createUserService } from '@cactus-agents/user';
const client = createApiClient({ baseUrl, tenant, language });
const fetcher = createFullFetcherFromClient(client);
const user = createUserService(fetcher);
Na maioria dos casos, prefira usar
createUserFromClientdiretamente — ele faz essa conversão internamente.
extractApiError(err)
Normaliza erros do ApiClient (que não são instâncias de Error) em uma estrutura consistente para logging e respostas HTTP:
import { extractApiError } from '@cactus-agents/api-client';
try {
await client.post('/endpoint', body);
} catch (err) {
const { status, data, message, isApiError } = extractApiError(err);
// status: number | undefined
// data: response body (se API error)
// message: string (para logging)
// isApiError: boolean
}
Tipos
interface ClientLike {
get<T>(path: string): Promise<{ data: T }>;
post<T>(path: string, body?: unknown): Promise<{ data: T }>;
}
interface FullClientLike extends ClientLike {
put<T>(path: string, payload?: unknown): Promise<{ data: T }>;
patch<T>(path: string, payload?: unknown): Promise<{ data: T }>;
delete<T>(path: string, payload?: unknown): Promise<{ data: T }>;
}
interface ExtractedApiError {
status: number | undefined;
data: unknown;
message: string;
isApiError: boolean;
url?: string;
statusText?: string;
}
interface ReferrerInfo {
/** URL completa do `document.referrer` external (cross-host) capturado no first-touch. */
referrer: string;
/** Hostname extraído do `referrer`. */
hostname: string;
/** Eco do response header `X-INFOS-ID` recebido em login/register, persistido em cookie pelo template. */
cinfoId?: string;
/** Eco do response header `X-INFOS-REF` recebido em login/register. */
cinfoIdRef?: string;
}
ReferrerInfocarrega referrer + IDs de tracking do BFF, não UTMs. UTMs viajam apenas como campos de body em signup/deposit (ver Marketing Tracking).
createCactusServerClient
Factory de alto nível para uso em Cloudflare Workers e SSR. Encapsula toda a lógica de headers da plataforma Cactus (IP real do cliente, geo CF, cookies, auth, origin-access) para que os loaders precisem apenas passar o env e o Request de entrada.
import { createCactusServerClient } from "@cactus-agents/api-client";
// Em um loader / action:
const client = createCactusServerClient({
env: context.cloudflare.env, // ou process.env em dev local
request,
getAccessToken: () => token,
});
// Agora use normalmente com os wrappers do SDK:
const games = createGamesFromClient(client);
const brand = await createBrandFromClient(client, opts);
CactusServerClientOptions
interface CactusServerClientOptions {
env: Partial<CactusServerEnv>; // vars de ambiente
request?: Request; // Request do Worker (extrai IP, geo, cookies, UA)
cookies?: string; // override de cookies (substitui request.cookies)
getAccessToken?: () => string | null | undefined;
buildId?: string;
debug?: boolean;
getOriginAccess?: () => OriginAccess; // default: OriginAccess.Desktop
getReferrerInfo?: () => ReferrerInfo | null;
// Hooks de erro (mesmos do ApiClientConfig)
onUnauthorized?: (url: string) => void;
onChallenge?: () => void;
onTimeoutLimit?: (url: string) => void;
}
CactusServerEnv
interface CactusServerEnv {
API_BASE_URL: string;
ORIGIN_DOMAIN: string;
BRAND_LANGUAGE: string;
BRAND_COUNTRY: string; // alpha-3 (ex: "BRA")
BRAND_CURRENCY: string;
BRAND_TIMEZONE: string;
CF_WORKER_KEY?: string; // header de autenticação Worker → API (server-only, nunca expor ao browser)
}
Headers injetados automaticamente
O createCactusServerClient injeta os seguintes headers em cada requisição, além dos headers padrão do ApiClient:
| Header | Origem |
|---|---|
User-Agent | request.headers["user-agent"] |
S-Client-Addr, x-cac-real-ip, X-Forwarded-For, X-Real-IP | CF-Connecting-IP do request |
x-country | request.cf.country (ISO alpha-2) |
x-user-info-timezone | request.cf.timezone |
x-user-info-long, x-user-info-lat | request.cf.longitude / request.cf.latitude |
x-user-info-region, x-user-info-city | request.cf.regionCode / request.cf.city |
country, country_alpha3, currency, jurisdiction | Derivados das env vars da marca |
Cookie | request.headers["Cookie"] (ou options.cookies) |
cf-worker-key | env.CF_WORKER_KEY (quando presente) |
CF_WORKER_KEY é um segredo server-side. Configure via wrangler secret put CF_WORKER_KEY e nunca inclua no bundle client.
RequestDebugInfo
Tipo da metadata da request capturada apenas quando debug: true no ApiClientConfig (anexada ao ApiResponse.requestInfo). Pra inspeção em DevApiDebug / DevApiExplorer. Não está relacionado ao header X-LOG-INFO, que é um string estável (1-{ts}-{buildId}).
interface RequestDebugInfo {
/** URL completa que foi chamada. */
url: string;
/** Método HTTP. */
method: string;
/** Headers enviados (Authorization e Cookie ficam mascarados como `[REDACTED]`). */
headers: Record<string, string>;
/** Body parseado, `undefined` para GET/bodyless. */
body?: unknown;
}
Exports
O pacote exporta os seguintes itens:
| Export | Tipo | Descrição |
|---|---|---|
ApiClient | class | Classe do client HTTP |
createApiClient | function | Factory básica para ApiClient (client-side / testes) |
createCactusServerClient | function | Factory para Workers/SSR com headers automáticos de plataforma |
createFetcherFromClient | function | Converte ApiClient para fetcher simples (get/post) |
createFullFetcherFromClient | function | Converte ApiClient para fetcher completo (5 métodos) |
extractApiError | function | Normaliza erros do ApiClient |
ApiClientConfig | type | Configuração base do client |
CactusServerClientOptions | type | Opções do createCactusServerClient |
CactusServerEnv | type | Shape das env vars esperadas pelo server client |
ApiResponse | type | Resposta padronizada |
ExtractedApiError | type | Erro normalizado |
RequestDebugInfo | type | Informações de debug do request |
OriginAccess | enum | Origem do acesso |
ReferrerInfo | type | Informações de referrer/UTM |
ClientLike | type | Interface mínima de client (get/post) |
FullClientLike | type | Interface completa de client (5 métodos) |