Saltar al contenido
Curso de Node.js y APIs
Todos los cursos Ir al ejercicio →

Consumir APIs con fetch y variables de entorno

  • ⏱ 60 minutos
  • 📶 Nivel: intermedio
  • 🧩 Incluye ejercicio

💡 🎯 Al terminar esta lección podrás…

  • Llamar a una API externa con fetch desde Node.
  • Comprobar respuesta.ok y manejar errores de red y de HTTP.
  • Enviar JSON y cabeceras con POST.
  • Construir URLs con URL y URLSearchParams.
  • Leer configuración y secretos con process.env y .env.

1.fetch también existe en Node

Hasta ahora tu servidor recibía peticiones. Muchas veces también necesitará hacerlas: pedir el clima a otro servicio, validar un pago, traer datos de otra API. Desde Node 18, la función fetch que ya conoces del navegador viene incluida, sin instalar nada. Está documentada en MDN y en nodejs.org.

pedir.js
const respuesta = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const dato = await respuesta.json();

console.log(respuesta.status);
console.log(dato);
Salida esperada
200
{ userId: 1, id: 1, title: 'delectus aut autem', completed: false }

Como tu proyecto usa ES Modules (lección 2), puedes escribir await directamente en el nivel superior del archivo, sin envolverlo en una función async.

📌 Nota · Es una API de pruebas

jsonplaceholder.typicode.com es un servicio público pensado para practicar: devuelve datos falsos y no guarda nada. Necesitas conexión a internet para ejecutar el ejemplo.

La respuesta (Response) trae tres cosas que mirarás siempre:

Propiedad o métodoQué es
respuesta.oktrue si el estado está entre 200 y 299
respuesta.statusEl código numérico (200, 404, 500…)
await respuesta.json()Lee el cuerpo y lo convierte de JSON a objeto (también hay .text())

2.fetch no falla con un 404

Este es el error más común: fetch solo rechaza la promesa si no consigue comunicarse (sin internet, dominio inexistente, servidor caído). Si el servidor responde con 404 o 500, fetch considera que todo fue bien porque sí hubo respuesta. Debes comprobar respuesta.ok tú mismo:

pedir.js
async function obtenerJson(url) {
  const respuesta = await fetch(url);

  if (!respuesta.ok) {
    throw new Error(`HTTP ${respuesta.status} al pedir ${url}`);
  }
  return respuesta.json();
}

try {
  const dato = await obtenerJson("https://jsonplaceholder.typicode.com/todos/99999");
  console.log(dato);
} catch (error) {
  console.error("No se pudo:", error.message);
}
Salida esperada
No se pudo: HTTP 404 al pedir https://jsonplaceholder.typicode.com/todos/99999

El patrón completo tiene tres capas: 1) try/catch para los fallos de red, 2) comprobar ok para los errores HTTP y 3) un mensaje claro que diga qué pasó y con qué dirección.

💡 Consejo · Pon un tiempo máximo

Una petición lenta puede dejar tu servidor esperando mucho. fetch(url, { signal: AbortSignal.timeout(5000) }) la cancela a los 5 segundos con un error.

3.Enviar datos y cabeceras

El segundo argumento de fetch es un objeto de opciones. Con él cambias el método, añades cabeceras y envías un cuerpo:

crear.js
const respuesta = await fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Hola", body: "Mi primer post", userId: 1 }),
});

console.log(respuesta.status);
console.log(await respuesta.json());
Salida esperada
201
{ title: 'Hola', body: 'Mi primer post', userId: 1, id: 101 }

Dos detalles importantes: el cuerpo debe ser texto, por eso se usa JSON.stringify, y la cabecera Content-Type avisa al servidor de que ese texto es JSON.

Construir la dirección con query

No concatenes textos a mano para armar una URL con parámetros: los espacios y símbolos romperían la dirección. Usa URLSearchParams, que codifica todo correctamente:

▶ URLSearchParams
Consola

URL y URLSearchParams están disponibles tanto en el navegador como en Node.

4.Variables de entorno con process.env

Las variables de entorno son valores que el sistema le pasa a tu programa desde fuera. Sirven para dos cosas: configurar (en qué puerto escuchar) y guardar secretos (claves de API, contraseñas) sin escribirlos en el código. En Node las lees con process.env, y siempre son texto:

config.js
const puerto = Number(process.env.PORT) || 3000;
const modo = process.env.NODE_ENV ?? "development";

console.log(`Puerto: ${puerto}, modo: ${modo}`);
Salida esperada
Puerto: 3000, modo: development

Para probar otros valores sin cambiar el código, defínelas al ejecutar:

Terminal (macOS, Linux, Git Bash)
PORT=8080 NODE_ENV=production node config.js
Salida esperada
Puerto: 8080, modo: production

⚠️ Importante · En Windows

En PowerShell se hace en dos pasos: $env:PORT = "8080" y luego node config.js. En cmd: set PORT=8080. Por eso casi todo el mundo usa un archivo .env, que funciona igual en todos los sistemas.

La pareja Number(valor) || 3000 es un patrón útil: si la variable no existe o no es un número, Number da NaN (que es «falso») y se usa el valor por defecto.

5.El archivo .env

Un archivo .env guarda variables, una por línea, en formato CLAVE=valor:

.env
# Configuración local (NO se sube a Git)
PORT=4000
API_KEY=abc123-clave-secreta
NODE_ENV=development

Node puede cargarlo sin instalar nada con la opción --env-file (disponible desde Node 20.6):

Terminal
node --env-file=.env index.js

Y puedes dejarlo fijo en los scripts de package.json para no olvidarlo:

package.json (fragmento)
"scripts": {
  "start": "node index.js",
  "dev": "node --watch --env-file=.env index.js"
}

Dentro del código, ya está en process.env:

index.js
console.log(process.env.API_KEY);   // "abc123-clave-secreta" 

📌 Nota · ¿Y el paquete dotenv?

Antes de existir --env-file todos instalaban el paquete dotenv (import "dotenv/config"). Sigue funcionando y lo verás en muchos proyectos, pero en Node moderno ya no hace falta.

Reglas de seguridad

.env.example
PORT=3000
API_KEY=pon-aqui-tu-clave
NODE_ENV=development

6.Todo junto: tu servidor consume una API

Un servidor intermediario es un caso muy habitual: tu API recibe una petición, consulta otro servicio con tu clave secreta y devuelve el resultado.

index.js
import express from "express";

const app = express();
const PORT = Number(process.env.PORT) || 3000;
const API_KEY = process.env.API_KEY;

if (!API_KEY) {
  console.error("Falta la variable API_KEY en .env");
  process.exit(1);
}

app.get("/pronostico/:ciudad", async (req, res) => {
  const url = new URL("https://api.ejemplo.com/clima");
  url.searchParams.set("ciudad", req.params.ciudad);

  const respuesta = await fetch(url, {
    headers: { Authorization: `Bearer ${API_KEY}` },
    signal: AbortSignal.timeout(5000),
  });

  if (!respuesta.ok) {
    return res.status(502).json({ error: "El servicio externo falló" });
  }
  res.json(await respuesta.json());
});

app.listen(PORT, () => console.log(`API en http://localhost:${PORT}`));

Observa tres decisiones: la clave se lee de process.env y el programa se detiene al arrancar si falta; la URL se construye con URL; y si el servicio externo falla se responde 502 Bad Gateway, el código que indica «el problema está en otro servidor». Gracias a Express 5, un error dentro de la función async también llegaría al manejador de errores.

📌 Nota · Ejemplo ilustrativo

api.ejemplo.com no existe: sustitúyela por una API real que tenga documentación y te dé una clave.

7.Errores frecuentes

📒 Apuntes para llevar

  • fetch viene incluido en Node desde la versión 18.
  • fetch rechaza solo por fallos de red; los errores HTTP se detectan con respuesta.ok o respuesta.status.
  • await respuesta.json() convierte el cuerpo en un objeto.
  • Para enviar JSON: method, headers con Content-Type y body: JSON.stringify(...).
  • URLSearchParams y URL arman direcciones con parámetros codificados.
  • process.env.NOMBRE lee variables de entorno, siempre como texto.
  • node --env-file=.env carga el archivo .env sin paquetes extra.
  • .env a .gitignore, .env.example al repositorio; las claves secretas solo van en el servidor.

8.Glosario

TérminoSignificado
fetchFunción que hace peticiones HTTP y devuelve una promesa.
ResponseObjeto con la respuesta: estado, cabeceras y cuerpo.
Variable de entornoValor de configuración que el sistema entrega al programa.
.envArchivo local con variables de entorno, una por línea.
API keyClave que identifica a quien usa una API y que debe mantenerse en secreto.
TimeoutTiempo máximo que se espera una respuesta antes de cancelar.
502 Bad GatewayCódigo que indica que un servidor intermedio recibió una respuesta inválida de otro.
Query stringParámetros de la URL que van tras el ?.

🧠 Comprueba lo aprendido

Elige una respuesta en cada pregunta. Verás al instante si acertaste y por qué.

¿Qué pasa con fetch si el servidor responde 404?

¿Qué hay que hacer para enviar un objeto como JSON?

¿De qué tipo es siempre process.env.PORT?

¿Cómo cargas un archivo .env sin instalar paquetes?

¿Qué archivo debe ir a .gitignore?

Tu progreso se guarda en este navegador y se refleja en el índice del curso.

💡 🚀 ¡Ahora te toca!

La práctica fija lo aprendido. Pasa al ejercicio de esta lección.

Ir al ejercicio