Consumir APIs con fetch y variables de entorno
💡 🎯 Al terminar esta lección podrás…
- Llamar a una API externa con
fetchdesde Node. - Comprobar
respuesta.oky manejar errores de red y de HTTP. - Enviar JSON y cabeceras con
POST. - Construir URLs con
URLyURLSearchParams. - Leer configuración y secretos con
process.envy.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.
const respuesta = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const dato = await respuesta.json();
console.log(respuesta.status);
console.log(dato);
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étodo | Qué es |
|---|---|
respuesta.ok | true si el estado está entre 200 y 299 |
respuesta.status | El 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:
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);
}
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:
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());
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:
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:
const puerto = Number(process.env.PORT) || 3000;
const modo = process.env.NODE_ENV ?? "development";
console.log(`Puerto: ${puerto}, modo: ${modo}`);
Puerto: 3000, modo: development
Para probar otros valores sin cambiar el código, defínelas al ejecutar:
PORT=8080 NODE_ENV=production node config.js
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:
# 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):
node --env-file=.env index.js
Y puedes dejarlo fijo en los scripts de package.json para no olvidarlo:
"scripts": {
"start": "node index.js",
"dev": "node --watch --env-file=.env index.js"
}
Dentro del código, ya está en process.env:
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
- Nunca subas
.enva Git. Añade.enva tu.gitignore(lección 3). Si una clave llega a un repositorio público, considérala robada y créala de nuevo. - Sube en su lugar un
.env.examplecon los nombres de las variables y valores falsos, para que otros sepan qué configurar. - Una clave en el código del navegador la ve cualquiera. Las claves secretas solo van en el servidor.
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.
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
- Creer que
fetchfalla con un 404: solo rechaza si no hay comunicación; compruebaok. - Olvidar
awaitantes defetcho de.json(): obtienes una promesa en lugar de datos. - Enviar un objeto sin
JSON.stringify: el cuerpo llega como[object Object]. - Olvidar la cabecera
Content-Type: application/json: el servidor no interpreta el cuerpo. - Subir
.enva Git: filtra tus claves. Añádelo a.gitignore. - Tratar
process.env.PORTcomo número: siempre es texto; usaNumber. - Concatenar parámetros a mano en la URL: usa
URLSearchParamspara codificarlos.
📒 Apuntes para llevar
fetchviene incluido en Node desde la versión 18.fetchrechaza solo por fallos de red; los errores HTTP se detectan conrespuesta.okorespuesta.status.await respuesta.json()convierte el cuerpo en un objeto.- Para enviar JSON:
method,headersconContent-Typeybody: JSON.stringify(...). URLSearchParamsyURLarman direcciones con parámetros codificados.process.env.NOMBRElee variables de entorno, siempre como texto.node --env-file=.envcarga el archivo.envsin paquetes extra..enva.gitignore,.env.exampleal repositorio; las claves secretas solo van en el servidor.
8.Glosario
| Término | Significado |
|---|---|
| fetch | Función que hace peticiones HTTP y devuelve una promesa. |
| Response | Objeto con la respuesta: estado, cabeceras y cuerpo. |
| Variable de entorno | Valor de configuración que el sistema entrega al programa. |
.env | Archivo local con variables de entorno, una por línea. |
| API key | Clave que identifica a quien usa una API y que debe mantenerse en secreto. |
| Timeout | Tiempo máximo que se espera una respuesta antes de cancelar. |
| 502 Bad Gateway | Código que indica que un servidor intermedio recibió una respuesta inválida de otro. |
| Query string | Pará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?
fetch solo rechaza si no logra comunicarse; con un 404 hay respuesta, así que debes mirar ok.
¿Qué hay que hacer para enviar un objeto como JSON?
El cuerpo debe ser texto y la cabecera avisa de que ese texto es JSON.
¿De qué tipo es siempre process.env.PORT?
Las variables de entorno son cadenas; conviértelas con Number si hace falta.
¿Cómo cargas un archivo .env sin instalar paquetes?
Desde Node 20.6 existe la opción --env-file.
¿Qué archivo debe ir a .gitignore?
.env contiene secretos reales y nunca debe subirse; el .env.example sí.
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.