Générateur de Client TypeScript Fetch Natif depuis OpenAPI & Swagger
Générez des clients d'API TypeScript en fetch natif sans dépendance externe, avec sérialisation des requêtes, en-têtes et typage complet.
/**
* 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;Confidentialité 100% Côté Client
Aucun envoi vers un serveur ni télémétrie. Vos contrats d'API confidentiels sont analysés exclusivement dans la mémoire locale de votre navigateur.
Fetch Natif Zéro Dépendance
Génère du code exploitant l'API Web Fetch standard, parfaitement compatible avec les navigateurs, Node.js 18+, Bun, Deno et Next.js sans dépendances externes.
Sérialisation et Typage Strict
Remplacement automatique des paramètres d'URL, sérialisation URLSearchParams, support multipart/form-data et gestion typée des erreurs ApiError.