OpenAPI & Swagger zu nativem Fetch TypeScript Client Generator
Erstellen Sie abhängigkeitsfreie native Fetch TypeScript API-Clients mit Abfrageparameter-Serialisierung, Headern und Antworttypen.
/**
* 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% Client-seitige Privatsphäre
Keine Server-Uploads oder Telemetrie. Ihre vertraulichen API-Spezifikationen werden ausschließlich im lokalen Browserspeicher verarbeitet.
Abhängigkeitsfreier nativer Fetch
Generiert reinen Web-Fetch-API-Code ohne externe Bibliotheken, sofort kompatibel mit Browsern, Node.js 18+, Bun, Deno und Next.js.
Vollständige typsichere Serialisierung
Automatische Pfadparameter-Interpolation, URLSearchParams, multipart/form-data und typisiertes ApiError-Handling.