Inicio / Artículos / Siete tareas de programación básica con Node.js y Express, resueltas y explicadas

Siete tareas de programación básica con Node.js y Express, resueltas y explicadas

Resuelve siete tareas típicas de entrevista para principiantes en Node.js, desde rutas y middleware de Express hasta manejo de errores, inversión de cadenas y detección de duplicados, y aprende a explicar cada una de ellas.

1640 palabras

Las entrevistas para puestos junior en Node.js y Express rara vez se centran en algoritmos complejos. Por lo general, te asignan tareas sencillas, como crear una ruta, escribir un middleware o limpiar un array, mientras el entrevistador observa cómo razonas y si puedes explicar por qué tu código funciona. Las siete tareas a continuación abarcan los temas más comunes; cada una incluye una solución funcional, un desglose de las líneas clave y puntos adicionales que demuestran una verdadera comprensión.

Tarea 1: Crear una ruta GET básica

Un ejercicio clásico de calentamiento: “Exponer un endpoint GET en / que responda con Hello World”. La solución consiste en crear una aplicación Express, registrar un manejador para la ruta raíz y comenzar a escuchar en un puerto.

import express from "express";

const app = express();

app.get("/", (req, res) => {
    res.status(200).send("Hello World");
});

app.listen(3000, () => {
    console.log("Server running on port 3000");
});

Dado que utiliza import, el proyecto necesita "type": "module" en package.json o una extensión .mjs.

Leyendo la ruta línea por línea

La llamada a app.get() registra un manejador que solo reacciona a las solicitudes GET:

app.get("/", (req, res) => {

El primer argumento, /, es la ruta. La función de callback recibe req, que describe la solicitud entrante, y res, utilizado para construir la respuesta.

La propia respuesta se genera aquí:

res.status(200).send("Hello World");

status(200) establece el código de estado (200 es el valor por defecto, pero ser explícito facilita la lectura), y send() escribe el cuerpo y finaliza la respuesta.

Finalmente, se inicia el servidor:

app.listen(3000, () => {

Esto vincula la aplicación al puerto 3000; la función de callback se ejecuta una vez que el servidor acepta conexiones. Los servicios reales suelen leer el puerto desde process.env.PORT.

Cómo expresarlo

"app.get() registra una ruta para las solicitudes GET. Cuando un cliente llama a /, Express ejecuta la función de callback, que responde con el estado 200 y el texto Hello World."

Tarea 2: Escribir un middleware para registrar solicitudes

La siguiente instrucción es: "Cree un middleware que registre cada solicitud entrante." Un middleware es una función con la firma (req, res, next). Este imprime el método y la URL, y luego pasa el control; app.use() lo aplica a todas las rutas registradas después de él.

const logger = (req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
};

app.use(logger);

Cada solicitud pasa por la cadena en orden antes de que un manejador de ruta responda:

Client
  ↓
Request
  ↓
Logger Middleware
  ↓
Route Handler
  ↓
Response

La línea más importante es la última dentro de la función:

next();

next() le indica a Express que este paso ya se ha completado y que debe ejecutarse el siguiente middleware o ruta. Si se olvida de hacerlo, la solicitud queda bloqueada hasta que el cliente agota el tiempo de espera, lo cual es una pregunta frecuente. La alternativa es finalizar la solicitud enviando una respuesta, como lo hace un middleware de autenticación cuando rechaza un token faltante.

Cómo expresarlo

"El middleware recibe la solicitud, la respuesta y next. Puede inspeccionar o modificar la solicitud, y luego responder o llamar a next() para pasar el control.

Tarea 3: Manejar trabajos asíncronos en una ruta

Los endpoints reales esperan a que las bases de datos y otras APIs respondan, por lo que es común preguntar “¿Cómo se maneja una operación asíncrona en Express?”. Una respuesta sólida es un manejador async con estructura try/catch:

app.get("/users", async (req, res, next) => {
    try {
        const users = await getUsers();
        res.status(200).json(users);
    } catch (error) {
        next(error);
    }
});

La línea clave es await:

const users = await getUsers();

Esto pausa al manejador hasta que getUsers() termine de ejecutarse, sin bloquear otras solicitudes. Si la promesa se rechaza, se ejecuta el bloque catch:

catch (error) {
    next(error);
}

next(error) transfiere el error al middleware de manejo de errores del Ejercicio 5. Cabe añadir que Express 4 no captura las promesas rechazadas de los manejadores asíncronos, por lo que try/catch es esencial en ese caso, mientras que Express 5 las maneja automáticamente. Para más información, consulte cómo crear un sistema de manejo de errores de nivel profesional en aplicaciones Node.js.

Tarea 4: Esbozar los puntos de extremo CRUD

CRUD significa Create, Read, Update y Delete, las cuatro operaciones básicas de datos, cada una asociada a un método HTTP. Ejemplos para el recurso users:

app.post("/users", (req, res) => {
    res.status(201).send("User created");
});

app.get("/users", (req, res) => {
    res.status(200).send("Users");
});
app.put("/users/:id", (req, res) => {
    res.status(200).send("User updated");
});
app.delete("/users/:id", (req, res) => {
    res.status(200).send("User deleted");
});

La creación devuelve 201 Created, y las rutas para un único usuario aceptan un parámetro :id obtenido de req.params.id. En lugar de memorizar líneas de código, entienda qué significa cada método:

POST   → Create
GET    → Read
PUT    → Update
DELETE → Delete

Esté preparado para distinguir entre PUT y PATCH: PUT reemplaza tradicionalmente todo el recurso, mientras que PATCH aplica cambios parciales.

Cómo expresarlo

"CRUD abarca las cuatro operaciones básicas de datos. En REST, POST crea, GET lee, PUT o PATCH actualiza, y DELETE elimina."

Tarea 5: Centralizar el manejo de errores

El manejo de errores suele realizarse después de la Tarea 3. Comience con una ruta que falle y redirija el error:

app.get("/", (req, res, next) => {
    try {
        throw new Error("Something went wrong");
    } catch (error) {
        next(error);
    }
});

Luego, agregue un manejador de errores que registre el incidente y devuelva un código 500 genérico:

app.use((err, req, res, next) => {
    console.error(err.message);
    res.status(500).json({
        message: "Internal Server Error"
    });
});

Lo que hace especial a esta función es su firma:

(err, req, res, next)

Express reconoce a los manejadores de errores por sus cuatro parámetros declarados, en el orden err, req, res, next; estos se ejecutan únicamente cuando se pasa un error. Registre el manejador después de sus rutas y evite enviar err.message a los clientes en entornos de producción. El flujo es el siguiente:

Route
  ↓
Error occurs
  ↓
catch(error)
  ↓
next(error)
  ↓
Error middleware
  ↓
500 Response

¿Es necesario usar next() dentro del manejador de errores?

No, si el manejador envía la respuesta por sí mismo:

app.use((err, req, res, next) => {
    console.error(err.message);
    res.status(500).send("Internal Server Error");
});

next debe declararse igualmente, ya que con tres parámetros Express trata a la función como un middleware ordinario.

Tarea 6: Invertir una cadena

Algunas preguntas son simples cuestiones de JavaScript: “Invertir la cadena hello”. La solución idiomática utiliza una sola línea que enlaza tres métodos:

const str = "hello";
const reversed = str.split("").reverse().join("");
console.log(reversed);

Esto imprime:

olleh

Qué hace cada paso

La función split genera los caracteres:

str.split("")

El resultado es un array:

["h", "e", "l", "l", "o"]

Luego el orden se invierte directamente en el array:

.reverse()

Esto da como resultado:

["o", "l", "l", "e", "h"]

Finalmente, los caracteres se unen sin ningún separador:

.join("")

Esto produce la cadena invertida:

"olleh"

Una variante común: “Ahora hazlo sin usar reverse()”. Se recorre el array desde el índice final hacia cero:

const str = "hello";
let reversed = "";
for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
}
console.log(reversed);

Ambos métodos son de complejidad lineal. Cabe mencionar que split("") y el acceso por índice funcionan con unidades de código UTF-16, por lo que los emojis se descomponen; en cambio, [...str] realiza la división por punto de código.

Tarea 7: Encontrar los duplicados en un array

Otra opción popular: “Devolver valores que aparecen más de una vez”. Dado:

const numbers = [1, 2, 3, 2, 4, 3, 5];

el resultado esperado es:

[2, 3]

Utilice dos objetos Set: uno para los valores ya vistos y otro para los duplicados confirmados:

const numbers = [1, 2, 3, 2, 4, 3, 5];
const seen = new Set();
const duplicates = new Set();
for (const number of numbers) {
    if (seen.has(number)) {
        duplicates.add(number);
    } else {
        seen.add(number);
    }
}
console.log([...duplicates]);

Al ejecutarlo, se registra lo siguiente:

[2, 3]

Por qué funcionan dos Sets

seen registra cada valor que se pasa. La primera aparición se agrega allí:

seen.add(number);

Una repetición hace que esta verificación tenga éxito:

if (seen.has(number))

El valor luego pasa a duplicates, y como un Set ignora las repeticiones, se informa una sola vez. Las búsquedas tienen un tiempo casi constante, por lo que la solución es lineal en lugar de utilizar bucles anidados cuadráticos. Para contar las ocurrencias, utilice un Map.

Analice su enfoque antes de escribir

La costumbre más importante es explicar tu plan antes de escribir código, ya que los entrevistadores evalúan tanto el razonamiento como los resultados. Para la tarea de duplicados, comienza con algo similar a lo siguiente:

"El plan consiste en un conjunto de valores ya vistos. Durante el bucle, cualquier valor que ya esté en él se coloca en un segundo conjunto de duplicados, lo que mantiene cada uno único."

Luego escribe el código. Esto demuestra comprensión en lugar de memorización y permite que el entrevistador te guíe desde el principio.

Lista de verificación para la preparación

Antes de una entrevista para posiciones junior en Node.js o Express, asegúrate de poder hacer cómodamente:

  • Definir rutas y explicar las funciones de req y res
  • Usar app.use() y escribir tu propio middleware
  • Explicar qué hace next() y qué ocurre cuando falta
  • Gestiona el trabajo asíncrono con async/await, try/catch y next(error)
  • Escribe un manejador de errores con cuatro parámetros y regístralo después de las rutas
  • Asocia las operaciones CRUD a métodos HTTP y códigos de estado adecuados
  • Manipula cadenas y arrays con los métodos básicos de JavaScript
  • Elije entre Set y Map para búsquedas y conteos
  • Narra tu enfoque mientras codificas
  • A este nivel, unos fundamentos sólidos, un código legible y explicaciones claras son mucho más importantes que algoritmos avanzados.

    Lecturas relacionadas

  • Asociar Tipos Temporales a Tareas Reales con Fechas: Siete Ejemplos Practicados — Resuelve siete tareas cotidianas relacionadas con fechas y horas utilizando JavaScript Temporal, desde fechas simples y desbordamiento de meses hasta zonas horarias, aritmética y validación de horario de verano.
  • Preparación para Intervistas en Node.js y Express: 70 Preguntas con Respuestas Precisas — Una revisión estructurada de 70 preguntas frecuentes en entrevistas sobre Node.js y Express, que abarcan desde el bucle de eventos y flujos hasta middleware, seguridad y escalabilidad, incluyendo correcciones a respuestas comunes.