Gerador de Cliente TypeScript Fetch Nativo a partir de OpenAPI e Swagger
Gere clientes de API em TypeScript com fetch nativo sem dependências externas, serialização de query string, cabeçalhos e tipagem completa.
/**
* Zero-Dependency Native Fetch TypeScript Client
* API: Swagger Petstore (v1.0.0)
* Base URL: https://petstore3.swagger.io/api/v3
* Standard Web Fetch API compatible with Browsers, Node.js 18+, Bun, Deno, and Next.js
*/
/**
* Generated TypeScript Interfaces & Models
* API: Swagger Petstore (v1.0.0)
* Spec: OpenAPI 3.0.3
* Generated automatically by ToolsAA Native Fetch Generator
*/
Category {
id?: number | undefined;
name?: string | undefined;
}
Tag {
id?: number | undefined;
name?: string | undefined;
}
Pet {
id: number;
name: string;
category?: Category | undefined;
photoUrls?: string[] | undefined;
tags?: Tag[] | undefined;
status?: 'available' | 'pending' | 'sold' | undefined;
}
ApiResponse {
code?: number | undefined;
type?: string | undefined;
message?: string | undefined;
}
// ============================================================================
// Operation Parameter Interfaces
// ============================================================================
ListPetsQueryParams {
/** Status values that need to be considered for filter */
status?: 'available' | 'pending' | 'sold' | undefined;
/** How many items to return at one time (max 100) */
limit?: number | undefined;
}
GetPetByIdPathParams {
/** The id of the pet to retrieve */
petId: number;
}
DeletePetPathParams {
/** Pet id to delete */
petId: number;
}
DeletePetHeaders {
api_key?: string | undefined;
}
UploadFilePathParams {
/** ID of pet to update */
petId: number;
}
UploadFileQueryParams {
additionalMetadata?: string | undefined;
}
// ============================================================================
// Runtime ApiError & Client Infrastructure
// ============================================================================
export class ApiError extends Error {
readonly status: number;
readonly statusText: string;
readonly data: unknown;
readonly headers: Headers;
constructor(status: number, statusText: string, data: unknown, headers: Headers) {
super(`HTTP ${status} ${statusText}`);
this.name = 'ApiError';
this.status = status;
this.statusText = statusText;
this.data = data;
this.headers = headers;
}
}
FetchClientConfig {
baseUrl?: string;
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
customFetch?: typeof fetch;
timeoutMs?: number;
onRequest?: (url: string, init: RequestInit) => void | Promise<void>;
onResponse?: (response: Response) => void | Promise<void>;
}
RequestOptions extends Omit<RequestInit, 'body' | 'headers'> {
headers?: Record<string, string>;
query?: Record<string, any>;
timeoutMs?: number;
}
ApiResponseEnvelope<T> {
data: T;
status: number;
headers: Headers;
}
function serializeQuery(query?: Record<string, any>): string {
if (!query) return '';
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value === undefined || value === null) continue;
if (Array.isArray(value)) {
value.forEach((v) => params.append(key, String(v)));
} else {
params.append(key, String(value));
}
}
const str = params.toString();
return str ? `?${str}` : '';
}
async function executeFetch<T>(
config: FetchClientConfig,
method: string,
path: string,
options?: RequestOptions & { body?: any }
): Promise<T> {
const baseUrl = (config.baseUrl || 'https://petstore3.swagger.io/api/v3').replace(/\/+$/, '');
const queryString = serializeQuery(options?.query);
const fullUrl = `${baseUrl}${path.startsWith('/') ? path : '/' + path}${queryString}`;
let dynamicHeaders: Record<string, string> = {};
if (typeof config.headers === 'function') {
dynamicHeaders = await config.headers();
} else if (config.headers) {
dynamicHeaders = config.headers;
}
const headers = new Headers({
...dynamicHeaders,
...options?.headers,
});
let requestBody: any = undefined;
if (options?.body !== undefined) {
if (typeof FormData !== 'undefined' && options.body instanceof FormData) {
requestBody = options.body;
// Browser sets multipart boundary automatically, remove conflicting content-type
headers.delete('Content-Type');
} else if (typeof Blob !== 'undefined' && options.body instanceof Blob) {
requestBody = options.body;
} else if (typeof options.body === 'object') {
if (!headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json');
}
requestBody = JSON.stringify(options.body);
} else {
requestBody = String(options.body);
}
}
const timeoutMs = options?.timeoutMs ?? config.timeoutMs ?? 15000;
let signal = options?.signal;
let timeoutId: any = null;
if (!signal && typeof AbortController !== 'undefined') {
const controller = new AbortController();
timeoutId = setTimeout(() => controller.abort(new Error('Request Timeout')), timeoutMs);
signal = controller.signal;
}
const requestInit: RequestInit = {
method,
headers,
body: requestBody,
signal,
...options,
};
if (config.onRequest) {
await config.onRequest(fullUrl, requestInit);
}
const fetchFn = config.customFetch || fetch;
try {
const response = await fetchFn(fullUrl, requestInit);
if (config.onResponse) {
await config.onResponse(response);
}
let responseData: any = null;
if (response.status !== 204) {
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
responseData = await response.json();
} else {
responseData = await response.text();
}
}
if (!response.ok) {
throw new ApiError(response.status, response.statusText, responseData, response.headers);
}
return responseData as T;
} finally {
if (timeoutId) clearTimeout(timeoutId);
}
}
// ============================================================================
// Flagship ApiClient Class
// ============================================================================
export class ApiClient {
private config: FetchClientConfig;
constructor(config: FetchClientConfig = {}) {
this.config = config;
}
public setBaseUrl(url: string): void {
this.config.baseUrl = url;
}
public setHeaders(headers: Record<string, string>): void {
this.config.headers = headers;
}
public setBearerToken(token: string): void {
this.config.headers = { ...((typeof this.config.headers === 'object' ? this.config.headers : {})), Authorization: `Bearer ${token}` };
}
public setApiKey(key: string, headerName: string = 'X-API-Key'): void {
this.config.headers = { ...((typeof this.config.headers === 'object' ? this.config.headers : {})), [headerName]: key };
}
/**
* List all pets
* @description Returns all pets from the store with optional status and tag filtering.
* @route GET /pets
* @param query URL search query parameters
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async listPets(query?: ListPetsQueryParams, options?: RequestOptions): Promise<Pet[]> {
const path = '/pets';
return executeFetch<Pet[]>(this.config, 'GET', path, { ...options, query });
}
/**
* Create a pet
* @description Adds a new pet to the catalog.
* @route POST /pets
* @param body Request payload (application/json)
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async createPet(body: Pet, options?: RequestOptions): Promise<Pet> {
const path = '/pets';
return executeFetch<Pet>(this.config, 'POST', path, { ...options, body });
}
/**
* Info for a specific pet
* @route GET /pets/{petId}
* @param pathParams URL path interpolation parameters
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async getPetById(pathParams: GetPetByIdPathParams, options?: RequestOptions): Promise<Pet> {
const path = `/pets/${encodeURIComponent(String(pathParams['petId']))}`;
return executeFetch<Pet>(this.config, 'GET', path, { ...options });
}
/**
* Deletes a pet
* @route DELETE /pets/{petId}
* @param pathParams URL path interpolation parameters
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async deletePet(pathParams: DeletePetPathParams, options?: RequestOptions): Promise<void> {
const path = `/pets/${encodeURIComponent(String(pathParams['petId']))}`;
return executeFetch<void>(this.config, 'DELETE', path, { ...options });
}
/**
* Uploads an image
* @route POST /pets/{petId}/uploadImage
* @param pathParams URL path interpolation parameters
* @param body Request payload (multipart/form-data)
* @param query URL search query parameters
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async uploadFile(pathParams: UploadFilePathParams, body?: {
/** File to upload @format binary */
file?: Blob | File | undefined;
}, query?: UploadFileQueryParams, options?: RequestOptions): Promise<ApiResponse> {
const path = `/pets/${encodeURIComponent(String(pathParams['petId']))}/uploadImage`;
return executeFetch<ApiResponse>(this.config, 'POST', path, { ...options, query, body });
}
/**
* Returns pet inventories by status
* @route GET /store/inventory
* @param options Additional RequestInit options
* @throws {ApiError}
*/
async getInventory(options?: RequestOptions): Promise<Record<string, number>> {
const path = '/store/inventory';
return executeFetch<Record<string, number>>(this.config, 'GET', path, { ...options });
}
}
api = new ApiClient();
export default ApiClient;100% de Privacidade no Navegador
Zero uploads ou telemetria externa. Suas especificações confidenciais são analisadas estritamente na memória local do navegador.
Fetch Nativo Sem Dependências
Gera código baseado na Web Fetch API padrão, compatível com navegadores modernos, Node.js 18+, Bun, Deno e Next.js Server Components.
Serialização Totalmente Tipada
Substituição automática de parâmetros de URL, URLSearchParams para query string, multipart/form-data e tratamento estruturado de ApiError.