Cliente HTTP em TypeScript: técnicas avançadas de requisições

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Cliente HTTP avançado em TypeScript: de requisições básicas a soluções industriais



No desenvolvimento de aplicações web modernas em TypeScript, o trabalho com requisições HTTP vai muito além de uma simples chamada fetch. Em nível avançado, é necessário não apenas enviar requisições, mas também tratar erros adequadamente, gerenciar tempos de espera, tipar respostas, implementar interceptadores e até mesmo armazenar dados em cache. Neste artigo, vamos abordar como construir um cliente HTTP robusto e flexível em TypeScript, adequado para projetos em produção.



1. Criação de um cliente HTTP tipado usando Genéricos (Generics)



Uma das principais vantagens do TypeScript é a tipagem estrita. Vamos criar uma classe base HttpClient que aceitará o tipo de resposta através de genéricos. Isso permitirá evitar erros em tempo de compilação.



// types.tsexport interface ApiResponse<T> {  data: T;  status: number;  ok: boolean;}

export interface RequestConfig { headers?: Record<string, string>; params?: Record<string, string>; signal?: AbortSignal;}

// http-client.tsimport { ApiResponse, RequestConfig } from './types';

class HttpClient { private baseUrl: string;

constructor(baseUrl: string) { this.baseUrl = baseUrl; }

async get<T>(endpoint: string, config?: RequestConfig): Promise<ApiResponse<T>> { const url = new URL(`${this.baseUrl}${endpoint}`); if (config?.params) { Object.entries(config.params).forEach(([key, value]) => { url.searchParams.append(key, value); }); }

const response = await fetch(url.toString(), { method: 'GET', headers: { 'Content-Type': 'application/json', ...config?.headers, }, signal: config?.signal, });

const data: T = await response.json(); return { data, status: response.status, ok: response.ok, }; }

async post<T, U>(endpoint: string, body: U, config?: RequestConfig): Promise<ApiResponse<T>> { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...config?.headers, }, body: JSON.stringify(body), signal: config?.signal, });

const data: T = await response.json(); return { data, status: response.status, ok: response.ok }; }}

export default HttpClient;


2. Tratamento de erros e timeouts



Um cliente avançado deve tratar corretamente tanto erros de rede quanto erros de lógica de negócio. Vamos adicionar um mecanismo de timeout usando AbortController e tratamento personalizado de erros.



// errors.tsexport class HttpError extends Error {  status: number;  data?: unknown;

constructor(message: string, status: number, data?: unknown) { super(message); this.name = 'HttpError'; this.status = status; this.data = data; }}

export class TimeoutError extends Error { constructor(timeout: number) { super(`Requisição expirou após ${timeout}ms`); this.name = 'TimeoutError'; }}

// http-client.ts (complemento)async requestWithTimeout<T>( endpoint: string, options: RequestInit, timeout: number = 5000): Promise<ApiResponse<T>> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout);

try { const response = await fetch(`${this.baseUrl}${endpoint}`, { ...options, signal: controller.signal, });

if (!response.ok) { const errorData = await response.json().catch(() => null); throw new HttpError( `HTTP ${response.status}: ${response.statusText}`, response.status, errorData ); }

const data: T = await response.json(); r

Blogs

Book Recommendations