Skip to content

Bun implementa el estándar WHATWG fetch, con algunas extensiones para satisfacer las necesidades de JavaScript del lado del servidor.

Bun también implementa node:http, pero fetch es generalmente recomendado en su lugar.

Enviar una solicitud HTTP

Para enviar una solicitud HTTP, usa fetch

ts
const response = await fetch("http://example.com");

console.log(response.status); // => 200

const text = await response.text(); // o response.json(), response.formData(), etc.

fetch también funciona con URLs HTTPS.

ts
const response = await fetch("https://example.com");

También puedes pasar a fetch un objeto Request.

ts
const request = new Request("http://example.com", {
  method: "POST",
  body: "¡Hola, mundo!",
});

const response = await fetch(request);

Enviar una solicitud POST

Para enviar una solicitud POST, pasa un objeto con la propiedad method establecida en "POST".

ts
const response = await fetch("http://example.com", {
  method: "POST",
  body: "¡Hola, mundo!",
});

body puede ser una cadena, un objeto FormData, un ArrayBuffer, un Blob, y más. Consulta la documentación de MDN para más información.

Proxy de solicitudes

Para hacer proxy de una solicitud, pasa un objeto con la propiedad proxy establecida en una cadena URL:

ts
const response = await fetch("http://example.com", {
  proxy: "http://proxy.com",
});

También puedes usar un formato de objeto para enviar encabezados personalizados al servidor proxy:

ts
const response = await fetch("http://example.com", {
  proxy: {
    url: "http://proxy.com",
    headers: {
      "Proxy-Authorization": "Bearer mi-token",
      "X-Custom-Proxy-Header": "valor",
    },
  },
});

Los headers se envían directamente al proxy en solicitudes CONNECT (para objetivos HTTPS) o en la solicitud proxy (para objetivos HTTP). Si proporcionas un encabezado Proxy-Authorization, sobrescribe cualquier credencial en la URL del proxy.

Encabezados personalizados

Para establecer encabezados personalizados, pasa un objeto con la propiedad headers establecida en un objeto.

ts
const response = await fetch("http://example.com", {
  headers: {
    "X-Custom-Header": "valor",
  },
});

También puedes establecer encabezados usando el objeto Headers.

ts
const headers = new Headers();
headers.append("X-Custom-Header", "valor");

const response = await fetch("http://example.com", {
  headers,
});

Cuerpos de respuesta

Para leer el cuerpo de la respuesta, usa uno de los siguientes métodos:

  • response.text(): Promise<string>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como una cadena.
  • response.json(): Promise<any>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como un objeto JSON.
  • response.formData(): Promise<FormData>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como un objeto FormData.
  • response.bytes(): Promise<Uint8Array>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como un Uint8Array.
  • response.arrayBuffer(): Promise<ArrayBuffer>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como un ArrayBuffer.
  • response.blob(): Promise<Blob>: Devuelve una promesa que se resuelve con el cuerpo de la respuesta como un Blob.

Cuerpos de respuesta en streaming

Puedes usar iteradores asíncronos para transmitir el cuerpo de la respuesta.

ts
const response = await fetch("http://example.com");

for await (const chunk of response.body) {
  console.log(chunk);
}

También puedes acceder más directamente al objeto ReadableStream.

ts
const response = await fetch("http://example.com");

const stream = response.body;

const reader = stream.getReader();
const { value, done } = await reader.read();

Cuerpos de solicitud en streaming

También puedes transmitir datos en cuerpos de solicitud usando un ReadableStream:

ts
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("Hola");
    controller.enqueue(" ");
    controller.enqueue("Mundo");
    controller.close();
  },
});

const response = await fetch("http://example.com", {
  method: "POST",
  body: stream,
});

Cuando usas streams con HTTP(S):

  • Los datos se transmiten directamente a la red sin almacenar en búfer el cuerpo completo en memoria
  • Si la conexión se pierde, el stream se cancelará
  • El encabezado Content-Length no se establece automáticamente a menos que el stream tenga un tamaño conocido

Cuando usas streams con S3:

  • Para solicitudes PUT/POST, Bun usa automáticamente carga multipart
  • El stream se consume en fragmentos y se carga en paralelo
  • El progreso se puede monitorear a través de las opciones de S3

Obtener una URL con tiempo de espera

Para obtener una URL con tiempo de espera, usa AbortSignal.timeout:

ts
const response = await fetch("http://example.com", {
  signal: AbortSignal.timeout(1000),
});

Cancelar una solicitud

Para cancelar una solicitud, usa un AbortController:

ts
const controller = new AbortController();

const response = await fetch("http://example.com", {
  signal: controller.signal,
});

controller.abort();

Sockets de dominio Unix

Para obtener una URL usando un socket de dominio Unix, usa la opción unix: string:

ts
const response = await fetch("https://hostname/a/path", {
  unix: "/var/run/path/to/unix.sock",
  method: "POST",
  body: JSON.stringify({ message: "¡Hola desde Bun!" }),
  headers: {
    "Content-Type": "application/json",
  },
});

TLS

Para usar un certificado de cliente, usa la opción tls:

ts
await fetch("https://example.com", {
  tls: {
    key: Bun.file("/ruta/a/key.pem"),
    cert: Bun.file("/ruta/a/cert.pem"),
    // ca: [Bun.file("/ruta/a/ca.pem")],
  },
});

Validación TLS Personalizada

Para personalizar la validación TLS, usa la opción checkServerIdentity en tls

ts
await fetch("https://example.com", {
  tls: {
    checkServerIdentity: (hostname, peerCertificate) => {
      // Devolver un Error si el certificado es inválido
    },
  },
});

Esto es similar a cómo funciona en el módulo net de Node.

Deshabilitar validación TLS

Para deshabilitar la validación TLS, establece rejectUnauthorized en false:

ts
await fetch("https://example.com", {
  tls: {
    rejectUnauthorized: false,
  },
});

Esto es especialmente útil para evitar errores SSL cuando usas certificados autofirmados, pero deshabilita la validación TLS y debe usarse con precaución.

Opciones de solicitud

Además de las opciones de fetch estándar, Bun proporciona varias extensiones:

ts
const response = await fetch("http://example.com", {
  // Controlar descompresión automática de respuesta (predeterminado: true)
  // Soporta gzip, deflate, brotli (br), y zstd
  decompress: true,

  // Deshabilitar reutilización de conexión para esta solicitud
  keepalive: false,

  // Nivel de registro de depuración
  verbose: true, // o "curl" para salida más detallada
});

Soporte de Protocolo

Más allá de HTTP(S), el fetch de Bun soporta varios protocolos adicionales:

URLs S3 - s3://

Bun soporta obtener de buckets S3 directamente.

ts
// Usar variables de entorno para credenciales
const response = await fetch("s3://mi-bucket/ruta/a/objeto");

// O pasar credenciales explícitamente
const response = await fetch("s3://mi-bucket/ruta/a/objeto", {
  s3: {
    accessKeyId: "TU_CLAVE_DE_ACCESO",
    secretAccessKey: "TU_CLAVE_SECRETA",
    region: "us-east-1",
  },
});

Nota: Solo los métodos PUT y POST soportan cuerpos de solicitud cuando se usa S3. Para cargas, Bun usa automáticamente carga multipart para cuerpos en streaming.

Puedes leer más sobre el soporte S3 de Bun en la documentación S3.

URLs de Archivo - file://

Puedes obtener archivos locales usando el protocolo file::

ts
const response = await fetch("file:///ruta/a/archivo.txt");
const text = await response.text();

En Windows, las rutas se normalizan automáticamente:

ts
// Ambos funcionan en Windows
const response = await fetch("file:///C:/ruta/a/archivo.txt");
const response2 = await fetch("file:///c:/ruta\\a/archivo.txt");

URLs de Datos - data:

Bun soporta el esquema de URL data::

ts
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "¡Hola, Mundo!"

URLs de Blob - blob:

Puedes obtener blobs usando URLs creadas por URL.createObjectURL():

ts
const blob = new Blob(["¡Hola, Mundo!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);

Manejo de Errores

La implementación fetch de Bun incluye varios casos de error específicos:

  • Usar un cuerpo de solicitud con métodos GET/HEAD lanzará un error (lo cual es esperado para la API fetch)
  • Intentar usar las opciones proxy y unix juntas lanzará un error
  • Fallos de validación de certificado TLS cuando rejectUnauthorized es true (o undefined)
  • Las operaciones S3 pueden lanzar errores específicos relacionados con autenticación o permisos

Manejo de Content-Type

Bun establece automáticamente el encabezado Content-Type para cuerpos de solicitud cuando no se proporciona explícitamente:

  • Para objetos Blob, usa el type del blob
  • Para FormData, establece el límite multipart apropiado

Depuración

Para ayudar con la depuración, puedes pasar verbose: true a fetch:

ts
const response = await fetch("http://example.com", {
  verbose: true,
});

Esto imprimirá los encabezados de solicitud y respuesta a tu terminal:

sh
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd

[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648

Nota: verbose: boolean no es parte de la API fetch estándar Web y es específico de Bun.

Rendimiento

Antes de que se pueda enviar una solicitud HTTP, se debe realizar la búsqueda DNS. Esto puede tomar una cantidad significativa de tiempo, especialmente si el servidor DNS es lento o la conexión de red es deficiente.

Después de la búsqueda DNS, se debe conectar el socket TCP y puede ser necesario realizar el handshake TLS. Esto también puede tomar una cantidad significativa de tiempo.

Después de que se completa la solicitud, consumir el cuerpo de la respuesta también puede tomar una cantidad significativa de tiempo y memoria.

En cada paso del camino, Bun proporciona APIs para ayudarte a optimizar el rendimiento de tu aplicación.

Prefetch DNS

Para hacer prefetch de una entrada DNS, puedes usar la API dns.prefetch. Esta API es útil cuando sabes que necesitarás conectarte a un host pronto y quieres evitar la búsqueda DNS inicial.

ts
import { dns } from "bun";

dns.prefetch("bun.com");

Caché DNS

Por defecto, Bun almacena en caché y deduplica consultas DNS en memoria por hasta 30 segundos. Puedes ver las estadísticas de la caché llamando a dns.getCacheStats():

Para aprender más sobre la caché DNS en Bun, consulta la documentación Caché DNS.

Preconectar a un host

Para preconnectar a un host, puedes usar la API fetch.preconnect. Esta API es útil cuando sabes que necesitarás conectarte a un host pronto y quieres iniciar la búsqueda DNS inicial, la conexión del socket TCP y el handshake TLS temprano.

ts
import { fetch } from "bun";

fetch.preconnect("https://bun.com");

Nota: llamar a fetch inmediatamente después de fetch.preconnect no hará que tu solicitud sea más rápida. Preconectar solo ayuda si sabes que necesitarás conectarte a un host pronto, pero no estás listo para hacer la solicitud todavía.

Preconectar al inicio

Para preconnectar a un host al inicio, puedes pasar --fetch-preconnect:

sh
bun --fetch-preconnect https://bun.com ./my-script.ts

Esto es como <link rel="preconnect"> en HTML.

Esta característica no está implementada en Windows todavía. Si estás interesado en usar esta característica en Windows, por favor abre un issue y podemos implementar soporte para ello en Windows.

Agrupación de conexiones y keep-alive HTTP

Bun reutiliza automáticamente las conexiones al mismo host. Esto se conoce como agrupación de conexiones. Esto puede reducir significativamente el tiempo que toma establecer una conexión. No necesitas hacer nada para habilitar esto; es automático.

Límite de conexiones simultáneas

Por defecto, Bun limita el número máximo de solicitudes fetch simultáneas a 256. Hacemos esto por varias razones:

  • Mejora la estabilidad general del sistema. Los sistemas operativos tienen un límite superior en el número de sockets TCP abiertos simultáneos, generalmente en los bajos miles. Acercarse a este límite hace que toda tu computadora se comporte extrañamente. Las aplicaciones se cuelgan y fallan.
  • Fomenta la reutilización de conexiones HTTP Keep-Alive. Para solicitudes HTTP de corta duración, el paso más lento es a menudo la configuración inicial de la conexión. Reutilizar conexiones puede ahorrar mucho tiempo.

Cuando se excede el límite, las solicitudes se encolan y se envían tan pronto como termina la siguiente solicitud.

Puedes aumentar el número máximo de conexiones simultáneas mediante la variable de entorno BUN_CONFIG_MAX_HTTP_REQUESTS:

sh
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts

El valor máximo para este límite está actualmente establecido en 65,336. El número de puerto máximo es 65,535, así que es bastante difícil para cualquier computadora exceder este límite.

Almacenamiento en búfer de respuesta

Bun hace grandes esfuerzos para optimizar el rendimiento de leer el cuerpo de la respuesta. La forma más rápida de leer el cuerpo de la respuesta es usar uno de estos métodos:

  • response.text(): Promise<string>
  • response.json(): Promise<any>
  • response.formData(): Promise<FormData>
  • response.bytes(): Promise<Uint8Array>
  • response.arrayBuffer(): Promise<ArrayBuffer>
  • response.blob(): Promise<Blob>

También puedes usar Bun.write para escribir el cuerpo de la respuesta en un archivo en disco:

ts
import { write } from "bun";

await write("output.txt", response);

Detalles de implementación

  • La agrupación de conexiones está habilitada por defecto pero se puede deshabilitar por solicitud con keepalive: false. El encabezado "Connection: close" también se puede usar para deshabilitar keep-alive.
  • Las cargas de archivos grandes se optimizan usando la llamada al sistema sendfile del sistema operativo bajo condiciones específicas:
    • El archivo debe ser más grande que 32KB
    • La solicitud no debe estar usando un proxy
    • En macOS, solo archivos regulares (no pipes, sockets o dispositivos) pueden usar sendfile
    • Cuando estas condiciones no se cumplen, o cuando se usa S3/cargas en streaming, Bun vuelve a leer el archivo en memoria
    • Esta optimización es particularmente efectiva para solicitudes HTTP (no HTTPS) donde el archivo se puede enviar directamente del kernel a la pila de red
  • Las operaciones S3 manejan automáticamente la firma de solicitudes y la fusión de encabezados de autenticación

Nota: Muchas de estas características son extensiones específicas de Bun a la API fetch estándar.

Bun por www.bunjs.com.cn editar