Menu
CoddyTech

Rutas asíncronas

Parte de la sección Conceptos básicos de Express del Journey de Node.js de Coddy. Lección 21 de 23.

Un arreglo desaparece cuando se reinicia el servidor. Un archivo conserva los datos.

import { readFile } from 'node:fs/promises';

app.get('/api/forecast', async (req, res) => {
    const text = await readFile('data/forecast.json', 'utf8');
    res.json(JSON.parse(text));
});

Una ruta puede ser una función async que espera con await, en este caso a readFile de node:fs/promises. Express 5 supervisa la promesa que devuelve la ruta: si se rechaza, porque falta el archivo o su JSON está dañado, el error llega a tu controlador de errores, como cuando se lanza una excepción.

Sin await, tienes una promesa en lugar de los datos, y res.json(promise) envía {}. El nuevo data/recipes.json contiene las cuatro recetas.

challenge icon

Desafío

Fácil

Un array en memoria se pierde cada vez que se reinicia el servidor. Guarda las recetas en un archivo en su lugar: data/recipes.json, que ya contiene las cuatro recetas.

  1. En routes/recipes.js, importa readFile y writeFile desde node:fs/promises, y deja de importar recipes desde ../data.js.
  2. Escribe una función auxiliar, loadRecipes(), una función asíncrona que lea data/recipes.json y devuelva la lista analizada. Las próximas lecciones la usarán con ese nombre.
  3. Haz que todas las rutas del archivo sean async, y empieza cada una esperando con await a loadRecipes(): /tags, la lista, una receta, la ruta POST y la ruta de escalado.
  4. En la ruta POST, después de añadir la nueva receta a la lista, escribe la lista completa de nuevo en data/recipes.json con writeFile, como texto JSON.
  5. La ruta de escalado todavía lanza un error si una receta no tiene porciones, ahora desde dentro de una función asíncrona. Express 5 pasa ese error a tu manejador de errores como antes, así que sigue respondiendo con 500 y {"error": "Something went wrong"}. No captures el error en la ruta.

Pruébalo: Pulsa Ejecutar y envía /api/recipes: las mismas cuatro recetas, ahora leídas del archivo. Elige POST y envía {"title": "Gazpacho", "tags": ["vegan", "soup"]} a /api/recipes: estado 201 y la receta con "id": 5. Envía /api/recipes/5: Gazpacho. Envía /api/recipes/99: estado 404. Después, envía por POST {"title": "Toast"}, que no tiene porciones y se convierte en la receta 6, y envía /api/recipes/6/scale?by=2: estado 500 y {"error":"Something went wrong"}. Si ves {} en lugar de tus datos, falta un await.

Pruébalo tú mismo

import express from 'express';
import { recipes } from '../data.js';

const router = express.Router();

// Cada etiqueta, una sola vez, en orden alfabético.
router.get('/tags', (req, res) => {
    const tags = new Set(recipes.flatMap((recipe) => recipe.tags));
    res.json([...tags].sort());
});

router.get('/', (req, res) => {
    res.json(recipes);
});

router.get('/:id', (req, res) => {
    const recipe = recipes.find((r) => r.id === Number(req.params.id));
    if (!recipe) {
        res.status(404).json({ error: 'No such recipe' });
        return;
    }
    res.json(recipe);
});

router.post('/', (req, res) => {
    const { title, tags } = req.body;
    if (!title) {
        res.status(400).json({ error: 'A recipe needs a title' });
        return;
    }
    const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
    recipes.push(recipe);
    res.status(201).json(recipe);
});

router.get('/:id/scale', (req, res) => {
    const recipe = recipes.find((r) => r.id === Number(req.params.id));
    if (!recipe) {
        res.status(404).json({ error: 'No such recipe' });
        return;
    }
    const by = Number(req.query.by);
    if (!Number.isFinite(by) || by <= 0) {
        res.status(400).json({ error: 'Scale by a positive number' });
        return;
    }
    if (!recipe.servings) {
        throw new Error(`Recipe ${recipe.id} has no servings`);
    }
    res.json({ title: recipe.title, servings: recipe.servings * by });
});

export default router;
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Conceptos básicos de Express

Practica por tu cuenta: Playground de Express