← Volver al listado de tecnologías

Ky: Cliente HTTP elegante para JavaScript

Por: Artiko
kyhttpfetchjavascripttypescript

Ky: Cliente HTTP Elegante

Ky es un cliente HTTP pequeño y elegante basado en la Fetch API. Ofrece una API más simple que fetch nativo, con atajos de métodos, manejo de errores para códigos no-2xx, reintentos automáticos y más.

Instalación

npm install ky
# o
bun add ky

Uso Básico

import ky from 'ky';

// GET simple
const data = await ky.get('https://api.example.com/users').json();

// POST con JSON
const user = await ky.post('https://api.example.com/users', {
  json: { name: 'Juan', email: '[email protected]' }
}).json();

// PUT y DELETE
await ky.put('https://api.example.com/users/1', { json: { name: 'Juan Updated' } });
await ky.delete('https://api.example.com/users/1');

Crear Instancia con Configuración

import ky from 'ky';

const api = ky.create({
  prefixUrl: 'https://api.example.com/v1',
  timeout: 30000,
  headers: {
    'Authorization': 'Bearer token123'
  }
});

// Usa la instancia configurada
const users = await api.get('users').json();

Reintentos Automáticos

const data = await ky.get('https://api.example.com/data', {
  retry: {
    limit: 3,
    methods: ['get', 'post'],
    statusCodes: [408, 429, 500, 502, 503, 504],
    backoffLimit: 10000
  }
}).json();

// Reintentar en timeout
const result = await ky.get('https://slow-api.example.com', {
  timeout: 5000,
  retry: {
    limit: 3,
    retryOnTimeout: true
  }
}).json();

Hooks

Ky permite interceptar requests en diferentes puntos:

const api = ky.create({
  hooks: {
    beforeRequest: [
      (request) => {
        request.headers.set('X-Custom-Header', 'value');
      }
    ],
    beforeRetry: [
      async ({ request, error, retryCount }) => {
        console.log(`Reintento ${retryCount}`);
        const newToken = await refreshToken();
        request.headers.set('Authorization', `Bearer ${newToken}`);
      }
    ],
    beforeError: [
      async (error) => {
        if (error.response) {
          const body = await error.response.json();
          error.message = body.message || error.message;
        }
        return error;
      }
    ]
  }
});

Manejo de Errores

import ky, { HTTPError, TimeoutError } from 'ky';

try {
  const data = await ky.get('https://api.example.com/data').json();
} catch (error) {
  if (error instanceof HTTPError) {
    const errorBody = await error.response.json();
    console.error(`Error ${error.response.status}:`, errorBody.message);
  } else if (error instanceof TimeoutError) {
    console.error('La petición excedió el tiempo límite');
  }
}

Timeout

// Timeout global
const api = ky.create({ timeout: 10000 });

// Timeout por petición
const data = await ky.get('https://api.example.com', {
  timeout: 5000
}).json();

// Sin timeout (útil para uploads)
await ky.post('https://api.example.com/upload', {
  body: formData,
  timeout: false
});

Query Parameters

const data = await ky.get('https://api.example.com/search', {
  searchParams: {
    query: 'javascript',
    page: 1,
    limit: 20
  }
}).json();
// URL: https://api.example.com/search?query=javascript&page=1&limit=20

Subida de Archivos

const formData = new FormData();
formData.append('file', file);
formData.append('name', 'mi-archivo');

const result = await ky.post('https://api.example.com/upload', {
  body: formData,
  timeout: false
}).json();

Ejemplo: Cliente API Completo

import ky, { HTTPError } from 'ky';

class APIClient {
  private api: typeof ky;
  private token: string | null = null;

  constructor(baseURL: string) {
    this.api = ky.create({
      prefixUrl: baseURL,
      timeout: 30000,
      retry: { limit: 3 },
      hooks: {
        beforeRequest: [
          (request) => {
            if (this.token) {
              request.headers.set('Authorization', `Bearer ${this.token}`);
            }
          }
        ]
      }
    });
  }

  setToken(token: string) {
    this.token = token;
  }

  async getUsers() {
    return this.api.get('users').json<User[]>();
  }

  async createUser(data: CreateUserDTO) {
    return this.api.post('users', { json: data }).json<User>();
  }
}

// Uso
const client = new APIClient('https://api.example.com/v1');
client.setToken('mi-token');
const users = await client.getUsers();

Ky vs Fetch vs Axios

CaracterísticaFetchKyAxios
TamañoNativo~4KB~13KB
ReintentosManualIncluidoPlugin
TimeoutManualIncluidoIncluido
HooksNoInterceptores
JSON automáticoNo

Recursos