Saltar al contenido
Chatbots de WhatsApp desde cero
Temario Ir al ejercicio →

JavaScript y Node.js para bots

  • ⏱ 60 minutos
  • 📶 Nivel: básico
  • 🧩 Incluye ejercicio

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

  • Instalar Node.js, comprobar la versión y crear un proyecto con npm init y ES Modules.
  • Usar map, filter, find y reduce sobre objetos y arrays como el catálogo de un minimarket.
  • Distinguir una función pura de una función con efectos y explicar por qué un bot gana al separarlas.
  • Escribir código con async/await, leer y crear JSON y llamar a una API con fetch.
  • Guardar secretos en variables de entorno y escribir una prueba con node:test.

1.Qué necesitas saber (y qué no)

Para construir el bot no hace falta dominar JavaScript: hace falta un subconjunto pequeño y bien conocido. Esta lección repasa solo lo que se usa en el resto del curso, con ejemplos del minimarket. Si en algún punto te pierdes, refuerza con estos dos cursos gratuitos:

Lo que verás aquí: la terminal, instalar Node, crear un proyecto, objetos y arrays, las cuatro funciones de arrays que más usarás, funciones puras, async/await, JSON, fetch, variables de entorno y pruebas.

2.Un editor para escribir código

Para escribir el código del bot necesitas un editor. Recomendamos Cursor: es un editor moderno con IA integrada, basado en Visual Studio Code (si ya usas VS Code, te sentirás en casa). Si no lo conoces, empieza por la guía oficial de Cursor. La terminal integrada se abre con Ctrl + `. Cualquier otro editor sirve.

📌 Nota · Usa la IA con criterio

Aunque el editor te sugiera código, entiende cada línea antes de aceptarla y nunca pegues tus claves o tokens en un chat con IA.

3.Instalar Node.js

Node.js es el programa que ejecuta JavaScript fuera del navegador. Tu bot será un programa de Node. Descarga la versión LTS (soporte a largo plazo) desde nodejs.org. Este curso se probó con una versión reciente (Node 24); sirve cualquier LTS actual. Los ejemplos usan node --test y --env-file, que existen desde Node 20.

Comprueba la instalación abriendo una terminal nueva (Símbolo del sistema o PowerShell en Windows; Terminal en macOS y Linux):

Terminal
node -v
npm -v
Salida esperada
v24.15.0

El número de versión será el tuyo; lo importante es que no diga «no se reconoce el comando». npm es el administrador de paquetes que viene con Node y mostrará su propio número.

⚠️ Importante · Verifica este dato

Los instaladores y comandos cambian con el tiempo. Si algo de esta sección no coincide con tu pantalla, sigue la guía oficial: nodejs.org/en/download.

4.La terminal en cinco comandos

Usarás la terminal todo el curso. Con cinco comandos alcanza:

Comando Qué hace
cd carpeta Entra en una carpeta (cd .. sube un nivel)
ls (macOS/Linux/PowerShell) o dir (Windows clásico) Lista los archivos
mkdir nombre Crea una carpeta
node archivo.js Ejecuta un archivo de JavaScript
Ctrl + C Detiene el programa que está corriendo

Crea tu carpeta de trabajo y entra en ella:

Terminal
mkdir bot-minimarket
cd bot-minimarket

5.Crear un proyecto con npm y ES Modules

Un proyecto de Node es una carpeta con un archivo package.json: su «cédula de identidad» (nombre, scripts, dependencias). Se crea con:

Terminal
npm init -y

Genera un package.json. Ábrelo y déjalo así (añadimos "type": "module" y dos scripts):

package.json
{
  "name": "bot-minimarket",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node consola.js",
    "test": "node --test"
  }
}

El campo "type": "module" activa los ES Modules: la forma moderna de dividir el código en archivos. Cada archivo exporta lo que ofrece y otros lo importan:

util.js
export function formatearSoles(n) {
  return "S/ " + n.toFixed(2);
}
index.js
import { formatearSoles } from "./util.js";

console.log(formatearSoles(2.5));
Salida esperada
S/ 2.50

Dos detalles que confunden a todos al empezar: en los imports de archivos propios debes escribir la extensión (./util.js) y la ruta empieza con ./. Los módulos propios de Node se importan con el prefijo node: (por ejemplo node:test, node:readline).

6.Objetos y arrays: la forma de los datos de un bot

Casi todo lo que maneja un bot son objetos (cosas con propiedades) en arrays (listas). El catálogo del minimarket es un array de objetos:

catalogo.js
export const catalogo = [
  { id: 1, nombre: "Gaseosa 500 ml", precio: 2.5, categoria: "bebidas" },
  { id: 2, nombre: "Pan francés", precio: 0.3, categoria: "panadería" },
  { id: 3, nombre: "Arroz 1 kg", precio: 4.8, categoria: "abarrotes" },
  { id: 4, nombre: "Agua 625 ml", precio: 1.5, categoria: "bebidas" },
];

Y un carrito es otro array: cada línea guarda el id del producto y la cantidad. Guardamos solo el id, no una copia del producto, para tener una sola fuente de verdad del precio.

JavaScript
const carrito = [
  { id: 1, cantidad: 2 },
  { id: 2, cantidad: 10 },
];

Accedes a una propiedad con punto (producto.precio) y a un elemento por su posición (catalogo[0]). Para copiar o combinar usa el operador de propagación: [...carrito, nuevaLinea] crea un array nuevo sin tocar el original.

7.map, filter, find y reduce

Estas cuatro funciones de array reemplazan casi todos los for. Cada una recibe una función que se aplica a cada elemento.

Función Qué devuelve Ejemplo en el minimarket
map Un array nuevo, del mismo largo, con cada elemento transformado Lista de nombres
filter Un array nuevo con los elementos que cumplen la condición Productos baratos
find El primer elemento que cumple (o undefined) Buscar por id
reduce Un solo valor acumulado Total del carrito

Las usamos sobre el catalogo de arriba (con const catalogo = [...] en el mismo archivo):

a.js
const nombres = catalogo.map((p) => p.nombre);
console.log(nombres);

console.log(catalogo.filter((p) => p.precio < 2).map((p) => p.nombre));

console.log(catalogo.find((p) => p.id === 3));
console.log(catalogo.find((p) => p.id === 99));

const total = carrito.reduce((suma, linea) => {
  const p = catalogo.find((x) => x.id === linea.id);
  return suma + p.precio * linea.cantidad;
}, 0);
console.log(total);
console.log("S/ " + total.toFixed(2));
Salida esperada
[ 'Gaseosa 500 ml', 'Pan francés', 'Arroz 1 kg', 'Agua 625 ml' ]
[ 'Pan francés', 'Agua 625 ml' ]
{ id: 3, nombre: 'Arroz 1 kg', precio: 4.8, categoria: 'abarrotes' }
undefined
8
S/ 8.00

reduce es la que más cuesta. Piensa en una caja registradora: empieza en 0 (segundo argumento), por cada línea suma su importe y entrega el total al final. suma es lo acumulado hasta ahora y linea el elemento actual; lo que devuelves en cada vuelta pasa como suma a la siguiente.

💡 Los decimales engañan

En JavaScript 0.1 + 0.2 da 0.30000000000000004. Para mostrar dinero usa toFixed(2); para guardar un total, redondea con Math.round(x * 100) / 100. En proyectos más grandes se guardan los montos en céntimos (enteros): S/ 2.50 es 250.

8.Funciones puras y funciones con efectos

Esta es la idea más importante de la lección, porque define cómo se organiza el bot completo.

JavaScript
// Pura: solo calcula
function totalCarrito(catalogo, carrito) {
  return carrito.reduce((suma, l) => {
    const p = catalogo.find((x) => x.id === l.id);
    return p ? suma + p.precio * l.cantidad : suma;
  }, 0);
}

// Con efecto: imprime
function mostrarTotal(total) {
  console.log("Total: S/ " + total.toFixed(2));
}

¿Por qué importa? Las funciones puras son fáciles de probar (les das un dato y miras el resultado) y fáciles de reutilizar. Tu bot tendrá un «cerebro» puro, que recibe el mensaje del cliente y devuelve la respuesta, y una «piel» con efectos, que lo conecta con la consola (lección 4) y luego con WhatsApp (lección 10). Si mezclas ambos, tendrás que enviar un WhatsApp real cada vez que quieras probar una regla.

📌 Nota · Una regla práctica

Escribe primero la función pura y llámala desde la que tiene efectos. Si te sorprendes escribiendo console.log o fetch dentro de una regla de negocio, sepáralos.

9.async/await: esperar sin bloquear

Algunas operaciones tardan: llamar a una API, leer un archivo, esperar una respuesta de Meta. En JavaScript devuelven una promesa (un valor que llegará más tarde). async/await te permite escribirlas como si fueran código normal:

b.js
const esperar = (ms) => new Promise((ok) => setTimeout(ok, ms));

async function main() {
  console.log("antes");
  await esperar(500);
  console.log("después de 0,5 s");
}

main();
console.log("esto sale primero que 'después'");
Salida esperada
antes
esto sale primero que 'después'
después de 0,5 s

Fíjate en el orden: main() empieza, llega al await y cede el control; mientras espera, el resto del programa sigue. Dos reglas:

  1. await solo se puede usar dentro de una función marcada async (en un módulo, también al nivel superior del archivo).
  2. Una función async siempre devuelve una promesa. Para obtener su valor, tienes que hacer await a su llamada.

Para manejar fallos, envuelve el await en try/catch:

JavaScript
try {
  const respuesta = await fetch("https://ejemplo.invalid/api");
  console.log(respuesta.status);
} catch (error) {
  console.log("No se pudo conectar:", error.message);
}

10.JSON: el idioma de las APIs

JSON es un texto con la misma forma que los objetos de JavaScript. Es el formato en que Meta te entrega cada mensaje. Dos funciones lo manejan:

JavaScript
const texto = JSON.stringify({ cliente: "51999000111", items: 2 });
console.log(texto);
console.log(JSON.parse(texto).items);
Salida esperada
{"cliente":"51999000111","items":2}
2

JSON.stringify convierte un objeto en texto y JSON.parse hace lo contrario. Dos trampas: el JSON exige comillas dobles en las claves y los textos, y JSON.parse lanza un error si el texto no es JSON válido (por eso, con datos que vienen de afuera, siempre conviene try/catch).

11.fetch: llamar a una API

fetch hace una petición HTTP y viene incluido en Node. Con él enviarás los mensajes a WhatsApp en la lección 14. Aquí un ejemplo de lectura (usa una API pública de prueba):

c.js
const r = await fetch("https://jsonplaceholder.typicode.com/todos/1");
console.log(r.status);
const dato = await r.json();
console.log(dato.title);
Salida esperada
200
delectus aut autem

Y así se ve un envío (POST) con cuerpo JSON. Es solo un esqueleto para que reconozcas la forma; no lo ejecutes todavía:

JavaScript
const r = await fetch("https://ejemplo.invalid/mensajes", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ texto: "hola" }),
});

await fetch(...) devuelve la respuesta; await r.json() lee su cuerpo como objeto. Revisa siempre r.ok o r.status antes de fiarte del resultado.

12.Variables de entorno y secretos

El bot necesitará secretos: el token de WhatsApp, claves de otras APIs. Nunca se escriben dentro del código (si subes el código a GitHub, todo el mundo las ve). Se guardan en variables de entorno y se leen con process.env:

env.js
console.log("TOKEN:", process.env.TOKEN_DE_PRUEBA ?? "(no definido)");

Sin definir la variable:

Terminal
node env.js
Salida esperada
TOKEN: (no definido)

Para definirla sin tocar el código, crea un archivo .env en la carpeta del proyecto y pásalo a Node:

.env
TOKEN_DE_PRUEBA=abc123
Terminal
node --env-file=.env env.js
Salida esperada
TOKEN: abc123

En la nube, donde publicarás el bot con Cloudflare, el equivalente local es un archivo llamado .dev.vars y los secretos reales se cargan desde el panel o la herramienta de despliegue (lección 11).

⚠️ Importante · Que el secreto no llegue a Git

Crea un archivo .gitignore con las líneas .env, .dev.vars y node_modules. Así Git no los sube. Si alguna vez publicas un token por error, revócalo y genera uno nuevo: borrar el commit no basta.

13.Pruebas con node:test

Una prueba automática es una función que llama a tu código con datos conocidos y verifica el resultado. Node trae un ejecutor sin instalar nada. Crea util.test.js junto a util.js:

util.test.js
import test from "node:test";
import assert from "node:assert/strict";
import { formatearSoles } from "./util.js";

test("formatea soles con dos decimales", () => {
  assert.equal(formatearSoles(2.5), "S/ 2.50");
});

test("este falla a propósito", () => {
  assert.equal(formatearSoles(1), "S/ 1");
});

Ejecuta todas las pruebas con node --test (o npm test). Con la prueba que falla a propósito:

Salida esperada
✔ formatea soles con dos decimales (1.2083ms)
✖ este falla a propósito (0.9698ms)
ℹ tests 2
ℹ pass 1
ℹ fail 1

✖ failing tests:

test at util.test.js:5:1
✖ este falla a propósito (0.9698ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:

  'S/ 1.00' !== 'S/ 1'

Lee el mensaje: dice cuál esperabas y cuál recibió. Corrige el valor esperado a "S/ 1.00" y las dos pasan. Los tiempos y detalles de la salida varían; lo que importa es la marca ✔ o ✖. En el resto del curso cada regla del bot tendrá su prueba: son tu red de seguridad cuando cambies algo.

14.Errores frecuentes

15.Apuntes para llevar

16.Glosario

Término Significado
Node.js Programa que ejecuta JavaScript fuera del navegador.
npm Administrador de paquetes y scripts que acompaña a Node.
package.json Archivo con el nombre, los scripts y las dependencias del proyecto.
ES Modules Sistema moderno de import/export para dividir el código en archivos.
Función pura Función que devuelve siempre lo mismo para los mismos datos y no tiene efectos.
Promesa Valor que llegará más tarde; await espera por él.
JSON Formato de texto para intercambiar datos, con la forma de un objeto de JavaScript.
Variable de entorno Valor de configuración fuera del código, leído con process.env.
node:test Ejecutor de pruebas incluido en Node.

🧠 Comprueba lo aprendido

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

¿Qué hace reduce en carrito.reduce((suma, l) => suma + l.cantidad, 0)?

¿Cuál es una función pura?

¿Qué necesitas en package.json para usar import y export en tus archivos .js?

¿Dónde debe guardarse el token de WhatsApp?

¿Qué obtienes si llamas a una función async sin usar await?

📚 Para profundizar (fuentes oficiales)

Este material lo hace un estudiante con ayuda de IA: ante cualquier duda, manda la documentación oficial.

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