L'aplicació d'Escena Viva té ara una estructura sòlida però una sola ruta. En aquesta lliçó l'omplim: recuperem els quatre endpoints de l'API del mòdul 4 (/salut, /esdeveniments, /esdeveniments/:id, /esdeveniments/:id/sessions), hi afegim les sessions i les comandes, i els organitzem en routers modulars amb controladors separats. Pel camí compararem cada mecanisme d'Express amb l'enrutador.js que vas escriure a mà, perquè la millor manera d'entendre app.get('/esdeveniments/:id') és recordar què costava fer-ho sense framework.

Contingut

  1. Mètodes de ruta i anatomia d'un gestor
  2. Paràmetres de ruta i req.params
  3. Paràmetres opcionals, comodins i expressions regulars
  4. Les altres dades de la petició: query, body, headers, ip
  5. express.Router(): routers modulars
  6. Reorganitzar l'API d'Escena Viva
  7. Controladors separats
  8. router.route() i router.param()
  9. L'ordre importa
  10. Els mètodes de res

  1. Mètodes de ruta i anatomia d'un gestor

Express exposa un mètode per cada verb HTTP, més all per a tots:

aplicacio.get('/esdeveniments', gestor);      // llegir una colleccio
aplicacio.post('/comandes', gestor);          // crear un recurs
aplicacio.put('/sessions/:id', gestor);       // reemplacar del tot
aplicacio.patch('/sessions/:id', gestor);     // modificar parcialment
aplicacio.delete('/comandes/:id', gestor);    // eliminar
aplicacio.all('/api/*resta', gestor);         // qualsevol metode

Cadascun registra una entrada a la pila interna d'Express: un patró, un mètode i una o diverses funcions. És literalment el que feia el teu enrutador.registrar(metode, patro, gestor), amb la diferència que Express admet diverses funcions per ruta: a aplicacio.post('/comandes', validarCos, comprovarAforament, crearComanda) les tres s'executen en ordre i cadascuna decideix si continua. Això ja és middleware, i és el tema de 06-04.

Anatomia d'un gestor

// peticio: IncomingMessage estes; resposta: ServerResponse estes.
function gestor(peticio, resposta, seguent) {
  // Tres sortides possibles:
  // 1. Respondre: resposta.json(...) / resposta.send(...) — la cadena acaba.
  // 2. Continuar: seguent() — passa al seguent gestor que encaixi.
  // 3. Fallar: seguent(error) — salta al gestor d'errors (06-07).
  resposta.json({ estat: 'ok' });
}

Una regla d'or: cada camí d'execució ha d'acabar en exactament una d'aquestes tres coses. Si un if no respon ni crida seguent(), la petició es queda penjada fins que el client es rendeix. Ho diagnosticarem a 06-04.

  1. Paràmetres de ruta i req.params

aplicacio.get('/esdeveniments/:id', (peticio, resposta) => {
  resposta.json({ idDemanat: peticio.params.id });
});
// curl -s localhost:3000/esdeveniments/evt-002  ->  {"idDemanat":"evt-002"}

Compara-ho amb el que feia el teu mòdul 4. compilarPatro('/esdeveniments/:id') recorria el patró, substituïa cada :nom per un grup de captura, desava els noms en un array i construïa la regex /^\/esdeveniments\/([^/]+)$/; després aparellar l'executava sobre la ruta i reconstruïa a mà l'objecte { id: 'evt-002' }. Express fa el mateix, amb diferències que importen:

Aspecte El teu compilarPatro Express (path-to-regexp)
Descodificació de la URL Cridaves decodeURIComponent tu Automàtica: /esdeveniments/Sala%20B%C3%B3veda arriba descodificat
Caràcters especials i casos límit Escapament a mà; risc de regex catastròfica Escapament segur i patrons acotats i provats
Múltiples paràmetres Funcionava, però el codi creixia /esdeveniments/:idEsdeveniment/sessions/:idSessio sense cost

Diversos paràmetres en una ruta:

aplicacio.get('/esdeveniments/:idEsdeveniment/sessions/:idSessio', (peticio, resposta) => {
  const { idEsdeveniment, idSessio } = peticio.params;
  resposta.json({ idEsdeveniment, idSessio });
});
// GET /esdeveniments/evt-001/sessions/ses-001-2
//   -> {"idEsdeveniment":"evt-001","idSessio":"ses-001-2"}

Important: req.params sempre conté cadenes. Si esperes un número, converteix-lo i valida'l. A 06-06 ho farem amb esquemes en comptes d'a mà.

  1. Paràmetres opcionals, comodins i expressions regulars

Aquí és on Express 5 va trencar amb Express 4; para atenció si et trobes codi antic.

Necessitat Express 4 Express 5
Paràmetre opcional /esdeveniments/:id? /esdeveniments{/:id}
Comodí /fitxers/* → req.params[0] Anònim no permès (llança error); amb nom, /fitxers/*ruta → req.params.ruta
Segment repetible /:seccio+ /*seccio (captura la resta)
// Parametre opcional: respon a /esdeveniments i a /esdeveniments/evt-001.
aplicacio.get('/esdeveniments{/:id}', (peticio, resposta) => {
  const { id } = peticio.params;
  resposta.json(id === undefined ? { tipus: 'colleccio' } : { tipus: 'element', id });
});

// Comodi amb nom: GET /descarregues/informes/2026/ocupacio.csv
//   -> peticio.params.ruta === 'informes/2026/ocupacio.csv'
aplicacio.get('/descarregues/*ruta', (peticio, resposta, seguent) => {
  try {
    // El comodi es la porta classica al recorregut de directoris:
    // reutilitzem resoldreDinsDe del modul 3.
    resposta.sendFile(resoldreDinsDe(DIRECTORI_INFORMES, peticio.params.ruta));
  } catch (error) {
    seguent(error); // codi RUTA_NO_PERMESA -> 403
  }
});

Fixa't en la lliçó que es repeteix: el comodí és còmode i perillós. Que Express extregui la ruta per tu no vol dir que sigui segura; resoldreDinsDe continua sent imprescindible.

Per a casos que la sintaxi de segments no cobreix, s'admet una expressió regular com a patró; amb ella, les captures arriben per índex numèric: aplicacio.get(/^\/esdeveniments\/(evt-\d{3})$/, (p, r) => r.json({ id: p.params[0] })). Es pot fer, però fes-ho amb moderació: una regex a la ruta és difícil de llegir i amaga una validació on ningú no la busca. És millor acceptar :id i validar el format amb un esquema (06-06), perquè així l'error que retornes és explicatiu en comptes d'un 404 mut.

  1. Les altres dades de la petició

Propietat Contingut Notes
req.params Paràmetres de ruta Sempre cadenes, ja descodificades
req.query Query string interpretada Només lectura a Express 5
req.body Cos interpretat undefined sense express.json() (06-04)
req.headers / req.get(n) Capçaleres en minúscules / una capçalera req.get('Content-Type') ignora majúscules
req.ip, req.protocol, req.secure IP i esquema del client Depenen de trust proxy (06-02)
req.method, req.path, req.originalUrl Mètode i ruta originalUrl conserva la URL completa abans de muntatges
// GET /api/esdeveniments?sala=Sala%20Boveda&pagina=2&ordenar=data
aplicacio.get('/api/esdeveniments', (peticio, resposta) => {
  const { sala, pagina = '1', ordenar = 'data' } = peticio.query;
  resposta.json({ sala, pagina: Number.parseInt(pagina, 10), ordenar });
});

L'error clàssic en migrar a Express 5

A Express 4, req.query era una propietat normal i molt codi «normalitzava» la query reassignant-la:

// FUNCIONAVA a Express 4 i FALLA a Express 5.
peticio.query = normalitzar(peticio.query); // TypeError: Cannot set property query

// Correcte a Express 5: el resultat normalitzat va a una altra propietat.
peticio.consulta = normalitzar(peticio.query);

A Express 5, req.query és un getter que interpreta la query string sota demanda i la memoritza; no té setter. Aquest és exactament el patró que farà servir el middleware validar() de 06-06 amb req.dadesValidades.

  1. express.Router(): routers modulars

Un Router és una mini-aplicació: té les seves rutes i el seu middleware, però no escolta en cap port. Es munta a l'aplicació (o en un altre router) sota un prefix.

const rutesEsdeveniments = express.Router();
rutesEsdeveniments.get('/', llistarEsdeveniments);     // ruta relativa
rutesEsdeveniments.get('/:id', obtenirEsdeveniment);   // ruta relativa
// El prefix es decideix en muntar, no en declarar.
aplicacio.use('/api/esdeveniments', rutesEsdeveniments);
// Resultat: GET /api/esdeveniments i GET /api/esdeveniments/:id

Els avantatges enfront de declarar-ho tot a app.js: rutes relatives (el router no sap on està muntat, així que canviar /api/esdeveniments per /api/v2/esdeveniments és una línia), middleware acotat (rutesComandes.use(limitador) afecta només les comandes), fitxers petits (un router per recurs, en comptes d'un app.js interminable) i reutilització (el mateix router es pot muntar dues vegades sota prefixos diferents).

  1. Reorganitzar l'API d'Escena Viva

// src/rutes/index.js — punt unic de muntatge.
const express = require('express');
const { rutesEsdeveniments } = require('./esdeveniments.js');
const { rutesSessions } = require('./sessions.js');
const { rutesComandes } = require('./comandes.js');
function crearRutesApi() {
  const api = express.Router();
  api.get('/salut', (p, r) => r.json({ estat: 'ok', instant: new Date().toISOString() }));
  api.use('/esdeveniments', rutesEsdeveniments);
  api.use('/sessions', rutesSessions);
  api.use('/comandes', rutesComandes);
  return api;
}

module.exports = { crearRutesApi };
// src/rutes/esdeveniments.js
const express = require('express');
const cc = require('../controladors/esdeveniments.js');

const rutesEsdeveniments = express.Router();

// S'executa una sola vegada per peticio que porti :id, abans del gestor.
rutesEsdeveniments.param('id', cc.carregarEsdeveniment);
rutesEsdeveniments.get('/', cc.llistarEsdeveniments);
rutesEsdeveniments.get('/:id', cc.obtenirEsdeveniment);
rutesEsdeveniments.get('/:id/sessions', cc.llistarSessionsDeLEsdeveniment);

module.exports = { rutesEsdeveniments };

// src/rutes/sessions.js i src/rutes/comandes.js segueixen el mateix patro.
rutesSessions.get('/:idSessio', obtenirSessio);
rutesSessions.get('/:idSessio/preus', obtenirPreusDeSessio);
rutesComandes.route('/').post(crearComanda);
rutesComandes.route('/:idComanda').get(obtenirComanda);

// I el muntatge a src/app.js:
aplicacio.use(express.json({ limit: configuracio.limitCos }));
aplicacio.use('/api', crearRutesApi());
aplicacio.use(express.static(configuracio.directoriPublic, { index: 'index.html' }));

Tota l'API penja de /api i el front-end estàtic de public/ es serveix a l'arrel. Un sol use decideix el versionat de l'API sencera.

  1. Controladors separats

El controlador tradueix HTTP a domini. No conté regles de negoci: crida el domini i decideix el codi d'estat.

// src/controladors/esdeveniments.js
const { obtenirCataleg, obtenirEsdevenimentPerId } = require('../cataleg-dades.js');

/** GET /api/esdeveniments — llistat resumit del cataleg. */
async function llistarEsdeveniments(peticio, resposta) {
  const esdeveniments = (await obtenirCataleg()).map((e) => ({
    id: e.id, titol: e.titol, sala: e.sala, nombreSessions: e.nombreSessions,
    aforamentTotal: e.aforamentTotal, entradesVenudes: e.entradesVenudes, exhaurit: e.exhaurit,
  }));
  resposta.json({ total: esdeveniments.length, esdeveniments });
}

// Middleware de router.param: carrega l'esdeveniment una sola vegada. Si no existeix,
// propaga l'error de domini (ESDEVENIMENT_NO_TROBAT -> 404).
async function carregarEsdeveniment(peticio, resposta, seguent, valorId) {
  peticio.esdeveniment = await obtenirEsdevenimentPerId(valorId);
  seguent();
}

/** GET /api/esdeveniments/:id — l'esdeveniment ja ve carregat per carregarEsdeveniment. */
const obtenirEsdeveniment = (peticio, resposta) => resposta.json(peticio.esdeveniment.toJSON());

/** GET /api/esdeveniments/:id/sessions */
function llistarSessionsDeLEsdeveniment(peticio, resposta) {
  const { id, nombreSessions, sessions } = peticio.esdeveniment;
  resposta.json({
    idEsdeveniment: id, total: nombreSessions,
    sessions: sessions.map((s) => ({
      id: s.id, inici: s.inici, aforament: s.aforament, lliures: s.lliures,
      exhaurida: s.exhaurida, preuCentims: s.preuCentims,
    })),
  });
}

module.exports = {
  llistarEsdeveniments, carregarEsdeveniment, obtenirEsdeveniment, llistarSessionsDeLEsdeveniment,
};

Tres coses que no veuràs en aquest fitxer, i és deliberat: no hi ha try/catch (a Express 5, si obtenirEsdevenimentPerId rebutja amb codi ESDEVENIMENT_NO_TROBAT, aquest rebuig arriba sol al gestor d'errors, que el traduirà a 404 amb la taula ESTAT_PER_CODI del mòdul 4; vegeu 06-07); no hi ha lectura de fitxers, que és de cataleg-dades.js; i no hi ha regles d'aforament, que són de domini/.

// src/controladors/comandes.js — POST /api/comandes
async function crearComanda(peticio, resposta) {
  // req.body existeix gracies a express.json() (06-04);
  // a 06-06 el substituirem per req.dadesValidades.
  const comanda = await registrarComanda(peticio.body);
  resposta.status(201).location(`/api/comandes/${comanda.id}`).json(comanda.toJSON());
}

  1. router.route() i router.param()

router.route()

Encadena diversos mètodes sobre la mateixa ruta sense repetir el patró: rutesSessions.route('/:idSessio').get(obtenirSessio).patch(actualitzarSessio).delete(cancellarSessio).

Avantatges: el patró s'escriu una vegada (menys risc que una còpia es desincronitzi) i queda visualment clar quines operacions admet el recurs. A més, Express genera automàticament el 405 Method Not Allowed amb la capçalera Allow correcta per als mètodes no registrats d'aquesta ruta — exactament el que tu vas programar a mà a enrutador.js.

router.param()

rutesEsdeveniments.param('id', carregarEsdeveniment) registra un middleware que s'executa una vegada per petició quan la ruta conté aquest paràmetre, abans de qualsevol gestor. Amb això, les tres rutes /, /:id i /:id/sessions comparteixen la càrrega de l'esdeveniment sense repetir ni una línia, i els controladors reben req.esdeveniment ja resolt.

A favor En contra
Elimina duplicació en rutes que comparteixen paràmetre i deixa els controladors molt nets El controlador depèn d'una cosa que no es veu al seu fitxer
Punt únic per al 404 del recurs S'executa també allà on potser no el necessites, i confon si dos routers fan servir el mateix nom amb semàntiques diferents

Compte important: router.param() és local al router on es registra. Si muntes rutesSessions en un altre lloc, el seu param no s'hereta. I si el mateix nom (:id) significa coses diferents en dos routers, fes servir noms diferents (:idEsdeveniment, :idSessio) com hem fet aquí.

  1. L'ordre importa

Express recorre la seva pila en l'ordre de registre i es queda amb la primera entrada que encaixi en mètode i ruta. Això explica el 90 % dels «la meva ruta no s'executa».

// MALAMENT: el comodi es declara primer i es menja tota la resta.
rutesEsdeveniments.get('/*resta', servirPaginaGenerica);
rutesEsdeveniments.get('/destacats', llistarDestacats);  // MAI no s'executa
rutesEsdeveniments.get('/:id', obtenirEsdeveniment);     // MAI no s'executa

// BE: del mes especific al mes generic.
rutesEsdeveniments.get('/destacats', llistarDestacats);  // literal
rutesEsdeveniments.get('/:id', obtenirEsdeveniment);     // parametre
rutesEsdeveniments.get('/*resta', servirPaginaGenerica); // comodi

Amb l'ordre dolent, una petició a /api/esdeveniments/destacats encaixa amb /*resta, que respon, i la cadena acaba aquí: les altres dues rutes són codi mort. La regla pràctica, en ordre de declaració: primer les rutes literals (/destacats, /salut), després les que porten paràmetre (/:id), tot seguit les de comodí (/*resta) i al final el 404. És el mateix problema que ja tenies al mòdul 4: el teu enrutador.js també recorria l'array de rutes en ordre. Express no ho resol per tu; n'hereta la mateixa regla.

La ruta 404 final

// Al final de src/app.js, despres de totes les rutes i estatics.
aplicacio.use((peticio, resposta, seguent) => {
  const missatge = `No existeix la ruta ${peticio.method} ${peticio.originalUrl}`;
  seguent(Object.assign(new Error(missatge), { codi: 'RUTA_NO_TROBADA' }));
});

És un use sense ruta, així que encaixa amb tot el que hi hagi arribat sense ser atès. No respon: propaga un error, perquè el gestor central de 06-07 doni la resposta en el format { error: { codi, missatge, estat } } com qualsevol altre error.

  1. Els mètodes de res

Mètode Què fa Què afegeix sobre node:http
res.json(objecte) Serialitza i envia JSON Fixa Content-Type i Content-Length, aplica json spaces, calcula ETag
res.send(valor) Envia cadena, Buffer o objecte Dedueix el Content-Type segons el tipus de l'argument
res.status(codi) Fixa l'estat Retorna res, així que s'encadena
res.sendStatus(codi) Estat + cos amb el text de l'estat res.sendStatus(204) respon en una línia
res.location(url) Fixa la capçalera Location Codifica la URL correctament
res.redirect([codi], url) Location + estat + cos Per defecte 302; fes servir 301 o 308 per a permanents
res.sendFile(rutaAbsoluta) Envia un fitxer ETag, Content-Type, rangs i streaming: és el teu estatics.js
res.set(n, v) / res.type(t) Capçaleres i Content-Type set admet un objecte; type accepta dreceres ('json')
res.end() Acaba la resposta El mateix de node:http, sense extres
// Exemples concrets a Escena Viva.
resposta.status(201).location(`/api/comandes/${comanda.id}`).json(comanda.toJSON());
resposta.sendStatus(204); // cancellacio acceptada, sense cos

// Redireccio permanent d'una URL antiga.
aplicacio.get('/esdeveniments/:id', (p, r) => r.redirect(301, `/api/esdeveniments/${p.params.id}`));

// Diverses capcaleres alhora.
resposta.set({ 'Cache-Control': 'public, max-age=60', 'X-Sala': 'Teatro Almendra' });

Comprova l'equivalència amb el mòdul 4: curl -si localhost:3000/api/esdeveniments/evt-001 retorna 200 OK amb Content-Type: application/json; charset=utf-8, Content-Length i un ETag: W/"19c-...". Aquest ETag el calculava el teu estatics.js a mà amb un hash; res.json() ho fa per defecte per a qualsevol resposta.

Errors Comuns i Consells

  • Declarar /:id abans que /destacats. El paràmetre captura la paraula literal i la ruta específica queda morta. Del més específic al més genèric, sempre.
  • Fer servir * sense nom a Express 5. Ja no està permès: l'aplicació falla en arrencar. Migra a *nom.
  • Reassignar req.query. TypeError a Express 5; desa el resultat normalitzat en una altra propietat.
  • Esperar que req.body existeixi sense express.json(). Serà undefined i obtindràs un TypeError: la fallada més comuna en escriure el primer POST.
  • Ficar lògica de negoci al controlador. Si calcula ocupacions o valida aforaments, aquesta lògica no es pot reutilitzar des dels informes ni provar sense HTTP.
  • Oblidar que router.param() és local al router: no s'hereta en muntar-lo en un altre lloc.
  • Confiar en un :id sense validar. Arriba com a cadena i pot ser qualsevol cosa. Un 404 mut no ajuda el client; a 06-06 retornarem un 400 explicatiu.
  • Consell: en desenvolupament, imprimeix la taula de rutes en arrencar recorrent aplicacio.router.stack. Veure l'ordre real de registre cura molts misteris.

Exercicis

Exercici 1: filtratge i paginació per query

Amplia GET /api/esdeveniments perquè admeti ?sala=, ?exhaurit=true|false, ?pagina= i ?perPagina= (per defecte 1 i 10, màxim 50). Retorna { pagina, perPagina, total, totalPagines, esdeveniments }. No reassignis req.query. Amb les dades de llavor, ?perPagina=2 ha de retornar 2 pàgines per als 3 esdeveniments.

Exercici 2: router de sessions amb route() i param()

Crea src/rutes/sessions.js amb un router.param('idSessio', carregarSessio) que cerqui la sessió recorrent el catàleg i la deixi a req.sessio (propagant SESSIO_NO_TROBADA si no existeix), i exposa amb router.route('/:idSessio') un GET que retorni el detall. Comprova que PUT /api/sessions/ses-001-1 retorna 405 amb la capçalera Allow.

Exercici 3: l'ordre que trenca

Escriu una aplicació amb tres rutes mal ordenades (/*resta, /destacats, /:id), demostra amb curl que només s'executa la primera, reordena-les i torna a provar. Afegeix-hi també un endpoint /depuracio/rutes que retorni la llista de rutes registrades en el seu ordre real.

Solucions

Solució 1

// src/controladors/esdeveniments.js (fragment)
const MAXIM_PER_PAGINA = 50;

async function llistarEsdeveniments(peticio, resposta) {
  // Lectura sense reassignar req.query; tota la conversio es manual.
  const { sala, exhaurit } = peticio.query;
  const pagina = Math.max(1, Number.parseInt(peticio.query.pagina ?? '1', 10) || 1);
  const brut = Number.parseInt(peticio.query.perPagina ?? '10', 10) || 10;
  const perPagina = Math.min(MAXIM_PER_PAGINA, Math.max(1, brut));

  let esdeveniments = await obtenirCataleg();
  if (sala) esdeveniments = esdeveniments.filter((e) => e.sala === sala);
  if (exhaurit === 'true' || exhaurit === 'false') {
    esdeveniments = esdeveniments.filter((e) => e.exhaurit === (exhaurit === 'true'));
  }

  const total = esdeveniments.length;
  const desDe = (pagina - 1) * perPagina;
  resposta.json({
    pagina, perPagina, total,
    totalPagines: Math.max(1, Math.ceil(total / perPagina)),
    esdeveniments: esdeveniments.slice(desDe, desDe + perPagina).map((e) => e.toJSON()),
  });
}

Tota aquesta conversió manual —parseInt, topalls, ?? '1'— és justament el que substituirem per un esquema a 06-06.

Solució 2

// src/controladors/sessions.js
async function carregarSessio(peticio, resposta, seguent, valorId) {
  const cataleg = await obtenirCataleg();
  for (const esdeveniment of cataleg) {
    const sessio = esdeveniment.cercarSessio(valorId);
    if (sessio) {
      peticio.sessio = sessio;
      peticio.esdevenimentDeLaSessio = esdeveniment;
      seguent();
      return;
    }
  }
  const missatge = `No existeix la sessio ${valorId}`;
  seguent(Object.assign(new Error(missatge), { codi: 'SESSIO_NO_TROBADA' }));
}

const obtenirSessio = (peticio, resposta) =>
  resposta.json({ ...peticio.sessio, idEsdeveniment: peticio.esdevenimentDeLaSessio.id });

module.exports = { carregarSessio, obtenirSessio };

I curl -si -X PUT localhost:3000/api/sessions/ses-001-1 retorna 405 Method Not Allowed amb Allow: GET, HEAD, generat per Express sense que tu escriguis res.

Solució 3

// ordre.js — versio que falla; reordena per comparar.
const aplicacio = require('express')();
aplicacio.get('/*resta', (p, r) => r.json({ ruta: 'comodi' }));
aplicacio.get('/destacats', (p, r) => r.json({ ruta: 'destacats' }));
aplicacio.get('/:id', (p, r) => r.json({ ruta: 'parametre' }));

// La llista de rutes en el seu ordre real es treu de la pila interna.
aplicacio.get('/depuracio/rutes', (peticio, resposta) => {
  resposta.json(
    aplicacio.router.stack.filter((c) => c.route).map((c) => c.route.path)
  );
});

aplicacio.listen(3000);

Amb aquest ordre, curl -s localhost:3000/destacats i curl -s localhost:3000/evt-001 retornen tots dos {"ruta":"comodi"}. Després de reordenar (destacats, :id, *resta) retornen {"ruta":"destacats"} i {"ruta":"parametre"}. Nota: /depuracio/rutes tampoc no s'arriba a assolir mentre el comodí estigui declarat abans; és el mateix exercici demostrant-se a si mateix.

Conclusió

Escena Viva ja té una API organitzada: tres routers muntats sota /api, controladors que només tradueixen HTTP a domini, router.param() carregant l'esdeveniment una sola vegada i deixant-lo a req.esdeveniment, i router.route() agrupant els mètodes de cada recurs amb el 405 i la seva capçalera Allow de franc. Has vist que :id i req.params són el teu compilarPatro més ben resolt, que els comodins d'Express 5 exigeixen nom i continuen necessitant resoldreDinsDe, que req.query és de només lectura i per què això trenca codi antic, i que l'ordre de declaració de les rutes és una regla que Express hereta, no que resol.

Queda un cap solt molt visible: POST /api/comandes llegeix req.body, i req.body no existeix per si sol: apareix únicament si abans de la ruta s'ha executat express.json(). Aquest «abans de la ruta» és la porta al concepte central del framework. A la lliçó següent, Middleware, veurem què és exactament aquesta cadena de funcions (req, res, next) que travessa cada petició: com es recorre, què signifiquen next(), next(error) i —l'error més frustrant d'Express— no cridar next() en absolut. Escriurem els middleware propis d'Escena Viva (registre amb durada, identificador de petició, control de memòria cau) i desmuntarem express.json() i express.static() comparant-los amb el cos.js i l'estatics.js que vas escriure a mà.

Curs de Node.js: De Principiant a Avançat

Mòdul 1: Introducció a Node.js

Mòdul 2: Conceptes Bàsics

Mòdul 3: Sistema de Fitxers i E/S

Mòdul 4: HTTP i Servidors Web

Mòdul 5: NPM i Gestió de Paquets

Mòdul 6: Framework Express.js

Mòdul 7: Bases de Dades i ORMs

Mòdul 8: Autenticació i Autorització

Mòdul 9: Proves i Depuració

Mòdul 10: Temes Avançats

Mòdul 11: Desplegament i DevOps

Mòdul 12: Projectes del Món Real

© Copyright 2026. Tots els drets reservats