Al final de la lliçó anterior va quedar un cap solt: POST /api/comandes llegeix req.body, però req.body no existeix tret que alguna cosa l'hagi creat abans. Aquesta «alguna cosa» és un middleware, i el middleware no és una característica més d'Express: és Express. Tot el que fa el framework —enrutar, llegir cossos, servir estàtics, gestionar errors— està construït sobre el mateix mecanisme: una llista de funcions (req, res, next) que es recorren en ordre i es van passant el testimoni. En aquesta lliçó desmuntem aquest mecanisme fins al fons, escrivim els middleware propis d'Escena Viva i comparem els integrats d'Express amb el cos.js i l'estatics.js que vas programar a mà al mòdul 4.

Contingut

  1. Què és un middleware
  2. El recorregut d'una petició
  3. next(), next(error) i no cridar next()
  4. Tipus de middleware
  5. L'ordre de registre és l'ordre d'execució
  6. Middleware amb ruta de muntatge
  7. Els middleware propis d'Escena Viva
  8. Middleware integrats: json, urlencoded, static
  9. Middleware asíncron
  10. Factories de middleware

  1. Què és un middleware

Un middleware és una funció amb aquesta signatura:

function elMeuMiddleware(peticio, resposta, seguent) {
  // Pot fer quatre coses:
  // 1. LLEGIR    la peticio i la resposta.
  // 2. MODIFICAR totes dues (afegir propietats, capcaleres...).
  // 3. CURTCIRCUITAR: respondre i acabar la cadena.
  // 4. PASSAR EL TESTIMONI amb seguent(), o desviar-se amb seguent(error).
  seguent();
}

// I es registra de tres maneres:
aplicacio.use(elMeuMiddleware);                              // per a totes les peticions
aplicacio.use('/api', elMeuMiddleware);                      // nomes sota /api
aplicacio.get('/api/esdeveniments', elMeuMiddleware, llistar); // nomes en aquesta ruta

La clau conceptual: una ruta també és un middleware. app.get('/salut', gestor) afegeix a la mateixa pila una entrada que, a més del mètode i el patró, té una funció amb la mateixa signatura; quan Express processa una petició recorre una única llista on conviuen middleware i rutes. Això ja ho vas fer al mòdul 4 sense anomenar-ho així: encadenaves llegirCos abans del gestor de POST /comandes i comprovaves el tipus de contingut, tot abans de la lògica. Express converteix aquest patró en el mecanisme central del framework.

  1. El recorregut d'una petició

flowchart TD
  A["Peticio HTTP entrant"] --> B["idPeticio<br/>(capcalera X-Id-Peticio)"]
  B --> C["registrePeticions<br/>(mesura amb res.on finish)"]
  C --> D["express.json()<br/>(omple req.body)"]
  D --> E{"Encaixa alguna ruta?"}
  E -- "si" --> F["Middleware de la ruta<br/>(validar, limitar...)"]
  F --> G["Controlador"]
  G --> H["res.json() → resposta"]
  E -- "no" --> I["Middleware 404"]
  I --> J["Gestor d'errors<br/>(err, req, res, next)"]
  F -- "seguent(error)" --> J
  G -- "throw / rebuig" --> J
  D -- "JSON malformat" --> J
  J --> K["Resposta d'error<br/>{ error: { codi, missatge, estat } }"]

Dos camins, un de normal i un d'error. Un middleware pot saltar del normal al d'error en qualsevol punt, i mai no es torna enrere: un cop dins del gestor d'errors, ja no s'executen els middleware normals que quedaven.

  1. next(), next(error) i no cridar next()

Aquestes opcions són tota la gramàtica del mecanisme.

Acció Efecte Quan fer-la servir
seguent() Continua amb l'entrada següent de la pila que encaixi El middleware ha fet la seva feina i la petició continua
seguent(error) Salta al primer middleware d'error Alguna cosa ha fallat i no pots continuar
seguent('router') Surt del router actual i continua al pare Poc freqüent: «aquest router no atén això»
Respondre sense seguent() Acaba la cadena aquí És el correcte en un controlador o en curtcircuitar
Ni respondre ni seguent() La petició es queda penjada Mai. Sempre és un error

L'error més frustrant del framework

// MALAMENT: hi ha un cami que no respon ni continua.
aplicacio.use((peticio, resposta, seguent) => {
  if (peticio.get('X-Api-Key')) seguent();
  // Si NO hi ha capcalera, no passa res. Ni resposta, ni error, ni seguent.
  // El client espera fins que expira el seu temps d'espera. Sense logs. Sense pistes.
});

// BE: tots els camins acaben.
aplicacio.use((peticio, resposta, seguent) => {
  if (!peticio.get('X-Api-Key')) {
    seguent(Object.assign(new Error('Falta la clau d\'API'), { codi: 'PARAMETRE_INVALID' }));
    return; // el return evita continuar executant per accident
  }
  seguent();
});

Símptomes de la versió dolenta: curl es queda quiet, el navegador mostra la rodeta eternament, no apareix cap error a la consola del servidor i el procés està perfectament sa. És desconcertant precisament perquè no hi ha cap error: simplement ningú no ha acabat la resposta. Per diagnosticar-ho:

// detectorDePenjades: registra'l el PRIMER en desenvolupament.
aplicacio.use((peticio, resposta, seguent) => {
  const temporitzador = setTimeout(() => {
    if (!resposta.headersSent) {
      console.error(`[penjada] ${peticio.method} ${peticio.originalUrl} porta 5s sense respondre`);
    }
  }, 5000);
  const netejar = () => clearTimeout(temporitzador);
  resposta.on('finish', netejar);
  resposta.on('close', netejar);
  seguent();
});

Així, una petició penjada deixa rastre a stderr amb el seu mètode i la seva ruta, i només has de mirar quin middleware d'aquesta ruta té un if sense sortida.

Regla: després de seguent(...), escriu return tret que sigui l'última línia. Cridar seguent() dues vegades provoca l'avís Cannot set headers after they are sent to the client, un altre clàssic difícil de rastrejar.

  1. Tipus de middleware

Tipus Com es registra Exemple a Escena Viva
D'aplicació / de router app.use(fn), app.get(ruta, fn, ...), router.use(fn) idPeticio, registrePeticions; limitador només a rutesComandes
D'error app.use((err, req, res, next) => ...) El gestor central de 06-07
Integrat / de tercers Ve amb Express o d'un paquet npm express.json, express.static; helmet, cors, morgan (06-05)

Tots són la mateixa cosa: funcions a la pila. L'única que es distingeix per la seva forma és la d'error, que té quatre paràmetres; Express compta els arguments (fn.length) per decidir si és normal o d'error, i per això oblidar el quart paràmetre fa que el teu gestor no s'executi mai (06-07).

  1. L'ordre de registre és l'ordre d'execució

No hi ha prioritats ni resolució intel·ligent: Express recorre la pila de dalt a baix, en l'ordre exacte en què vas registrar cada entrada.

// MALAMENT: la ruta es registra ABANS del lector de cos.
aplicacio.post('/api/comandes', (peticio, resposta) => {
  // peticio.body es undefined: express.json() encara no s'ha executat.
  resposta.json({ rebut: peticio.body.sessioId });
  // TypeError: Cannot read properties of undefined (reading 'sessioId')  -> 500
});
aplicacio.use(express.json());

// BE: primer el transversal, despres les rutes.
aplicacio.use(express.json());              // 1. omple req.body
aplicacio.post('/api/comandes', (peticio, resposta) => {
  resposta.json({ rebut: peticio.body.sessioId }); // 2. ja existeix
});

A la versió dolenta el middleware està registrat, sí, però després de la ruta: com que la ruta respon, la cadena acaba abans d'arribar-hi.

L'ordre canònic de crearAplicacio(), que anirem completant i tancarem a 06-05: ajustos (disable('x-powered-by'), set('trust proxy')), seguretat i capçaleres (helmet, cors), observabilitat (idPeticio, registrePeticions), anàlisi del cos (express.json), estàtics (express.static), rutes de l'API, middleware 404 i, sempre l'últim, el gestor d'errors.

  1. Middleware amb ruta de muntatge

aplicacio.use(fn) s'executa sempre; aplicacio.use('/api', fn) només si la ruta comença per /api. El muntatge amb prefix té un efecte que sorprèn: dins del middleware muntat, req.url perd el prefix.

Dins d'un middleware muntat a /api, per a GET /api/esdeveniments/evt-001?x=1:

Propietat Contingut Fes-la servir per a
req.originalUrl /api/esdeveniments/evt-001?x=1 (sempre completa) Registres i traces
req.baseUrl /api (el punt de muntatge) Construir URL absolutes
req.url /esdeveniments/evt-001?x=1 (relativa al muntatge) Lògica interna del router
req.path /esdeveniments/evt-001 (sense query) Comparacions de ruta

Aquesta reescriptura és el que permet que src/rutes/esdeveniments.js declari router.get('/:id') sense saber que estarà muntat a /api/esdeveniments. És la mateixa tècnica que fa servir express.static: muntat a /descarregues, cerca al disc només la part relativa.

Consell de registre: a registre-peticions.js fes servir sempre req.originalUrl. Amb req.url dins d'un muntatge, els teus logs mostraran rutes incompletes i no les podràs correlacionar amb el que veu el client.

  1. Els middleware propis d'Escena Viva

src/middleware/id-peticio.js

Genera un identificador únic per petició: la base de la traçabilitat, que apareixerà als registres, a les respostes d'error (06-07) i al registre estructurat del mòdul 11.

// src/middleware/id-peticio.js
const { randomUUID } = require('node:crypto');
const CAPCALERA = 'X-Id-Peticio';

// Assigna un identificador unic a cada peticio. Si el client (o un proxy)
// ja n'ha enviat un de valid, el respecta, perque la traca sobrevisqui a diversos serveis.
function idPeticio(peticio, resposta, seguent) {
  const rebut = peticio.get(CAPCALERA);
  const esValid = typeof rebut === 'string' && /^[\w-]{8,64}$/.test(rebut);
  const identificador = esValid ? rebut : randomUUID();
  peticio.idPeticio = identificador;
  resposta.locals.idPeticio = identificador;
  resposta.setHeader(CAPCALERA, identificador);
  seguent();
}

module.exports = { idPeticio, CAPCALERA_ID_PETICIO: CAPCALERA };

Detall important: es valida l'identificador rebut. Acceptar a cegues una capçalera del client permetria injectar salts de línia als registres o valors enormes; la regex acota longitud i caràcters.

src/middleware/registre-peticions.js

Mesura la durada real de cada petició i l'escriu per stderr, segons la convenció del curs.

// src/middleware/registre-peticions.js
// Registra metode, ruta, estat, mida i duracio de cada peticio.
function crearRegistrePeticions({ ometreRutes = ['/api/salut'] } = {}) {
  return function registrePeticions(peticio, resposta, seguent) {
    if (ometreRutes.includes(peticio.originalUrl)) {
      seguent();
      return;
    }
    // hrtime.bigint dona nanosegons monotons: no l'afecten els canvis d'hora.
    const inici = process.hrtime.bigint();
    const etiqueta = `${peticio.idPeticio ?? '-'} ${peticio.method} ${peticio.originalUrl}`;

    // 'finish' es dispara quan la resposta s'ha lliurat del tot.
    resposta.on('finish', () => {
      const ms = (Number(process.hrtime.bigint() - inici) / 1e6).toFixed(1);
      const bytes = resposta.getHeader('Content-Length') ?? 0;
      console.error(`[peticio] ${etiqueta} ${resposta.statusCode} ${bytes}b ${ms}ms`);
    });

    // 'close' sense 'finish' vol dir que el client ha avortat abans d'hora.
    resposta.on('close', () => {
      if (!resposta.writableFinished) console.error(`[peticio] ${etiqueta} AVORTADA`);
    });
    seguent();
  };
}

Per què res.on('finish') i no mesurar després de seguent(): perquè seguent() retorna el control tan bon punt el middleware següent se suspèn en una operació asíncrona, no quan la resposta s'ha enviat. L'esdeveniment finish de ServerResponse —el mateix objecte stream del mòdul 3— és l'únic punt fiable.

src/middleware/sense-cache.js

// src/middleware/sense-cache.js
// Marca com a no cacheable el que canvia constantment: aforaments i comandes.
function senseCache(peticio, resposta, seguent) {
  const capcaleres = { 'Cache-Control': 'no-store, no-cache, must-revalidate', Pragma: 'no-cache' };
  resposta.set({ ...capcaleres, Expires: '0' });
  seguent();
}

module.exports = { senseCache };

// Es munta nomes on cal, no globalment (src/rutes/index.js):
api.use('/comandes', senseCache, rutesComandes);
api.use('/sessions', senseCache, rutesSessions); // l'aforament canvia a cada venda
api.use('/esdeveniments', rutesEsdeveniments);   // el cataleg si que es pot cachejar

Que la disponibilitat de la Sala Bóveda se serveixi des d'una memòria cau de trenta segons és exactament com es ven una entrada que ja no existeix.

A src/app.js queden registrats en aquest ordre: aplicacio.use(idPeticio) primer, perquè tota la resta el fa servir; aplicacio.use(crearRegistrePeticions()) segon, per mesurar des del més aviat possible; i després express.json({ limit }).

  1. Middleware integrats

express.json() enfront del teu cos.js

Es configura amb express.json({ limit: '100kb', type: 'application/json', strict: true }): limit equival al teu LIMIT_BYTES, type acota quin Content-Type interpreta i strict accepta només objectes i arrays a l'arrel. Comparació honesta amb el que vas escriure al mòdul 4:

Aspecte El teu llegirCos express.json()
Acumular els trossos del stream req.on('data') i concatenar Buffers Igual, internament
Límit de mida i resposta en superar-lo LIMIT_BYTES i COS_MASSA_GRAN → 413 Opció limit; error amb status: 413 i type: 'entity.too.large'
JSON malformat JSON_INVALID → 400 Error amb status: 400 i type: 'entity.parse.failed'
Content-Type incorrecte TIPUS_NO_ACCEPTAT → 415 No falla: deixa req.body com a {}
Codificacions i cos cru No ho vas cobrir charset i gzip inclosos; opció verify per a signatures de webhooks

És el teu mateix disseny amb més casos límit coberts. Un cos que supera el límit retorna 413 i un JSON trencat retorna 400; aquests errors arriben al gestor central com qualsevol altre, i a 06-07 els traduirem al nostre format mirant la seva propietat type. Un detall que sorprèn: si el Content-Type no és JSON, express.json() no protesta, simplement no fa res i req.body queda com a {}. Si vols el 415 del mòdul 4, cal exigir-lo explícitament:

// src/middleware/exigir-json.js
function exigirJson(peticio, resposta, seguent) {
  if (['GET', 'HEAD', 'DELETE'].includes(peticio.method) || peticio.is('application/json')) {
    seguent();
    return;
  }
  const missatge = 'S\'esperava Content-Type: application/json';
  seguent(Object.assign(new Error(missatge), { codi: 'TIPUS_NO_ACCEPTAT' }));
}

express.urlencoded()

Interpreta formularis HTML clàssics amb aplicacio.use(express.urlencoded({ extended: true, limit: '20kb' })). Amb extended: true fa servir la biblioteca qs i admet estructures imbricades (filtre[sala]=Sala+Boveda); amb false fa servir querystring del nucli i tot queda pla. Escena Viva és una API JSON i no ho necessita, però convé conèixer-ho per si hi afegeixes un formulari de contacte.

express.static() enfront dels teus estatics.js + tipus-mime.js

aplicacio.use(express.static(configuracio.directoriPublic, {
  index: 'index.html',   // que servir al directori arrel
  maxAge: '1h',          // Cache-Control: public, max-age=3600
  etag: true,            // ETag i 304 automatic (amb lastModified, If-Modified-Since)
  dotfiles: 'ignore',    // no serveix .env ni .git
  fallthrough: true,     // si el fitxer no existeix, continua la cadena (404 propi)
  extensions: ['html'],  // /contacte troba contacte.html
}));
El que feies a mà El que fa express.static
resoldreDinsDe contra el recorregut de directoris Comprovació equivalent incorporada
Mapa d'extensió a MIME (tipus-mime.js) mime-types, amb centenars de tipus
createReadStream + pipeline Igual, amb suport de rangs (Range) per a vídeo
Hash del contingut per a l'ETag i comparar If-None-Match ETag feble per mida i data (més barat) i 304 automàtic
Comprimir amb gzip a mà No ho fa: és feina de compression (06-05)

El que fa millor que la teva versió: rangs HTTP, dotfiles, fallthrough (que permet que un fitxer inexistent continuï cap al teu 404 en comptes de respondre allà mateix) i una llista de tipus MIME que mai no voldràs mantenir a mà. El que la teva versió feia i aquesta no: la compressió, delegada a un middleware específic. És una separació de responsabilitats millor que la teva.

  1. Middleware asíncron

Un middleware pot ser async i a Express 5 això simplement funciona: si obtenirCataleg rebutja (fitxer illegible, JSON corrupte) a async function carregarCataleg(peticio, resposta, seguent) { peticio.cataleg = await obtenirCataleg(); seguent(); }, Express captura la promesa rebutjada i crida seguent(error) per tu.

A Express 4 aquest codi deixava la petició penjada si la promesa rebutjava, i calia envoltar cada gestor amb const asyncHandler = (fn) => (p, r, s) => Promise.resolve(fn(p, r, s)).catch(s). Si et trobes asyncHandler, express-async-handler o wrapAsync en un projecte, ja saps què són: restes d'Express 4 que a Express 5 sobren. Hi aprofundirem a 06-07.

L'excepció que continua sent teva: les devolucions de trucada dins d'un gestor. Express només pot capturar el que rebutja la promesa que retornes; no veu un throw dins d'un setTimeout ni dins d'un stream.on('data').

// MALAMENT: Express no pot capturar aixo. Tomba el proces.
aplicacio.get('/malament', (peticio, resposta) => {
  setTimeout(() => { throw new Error('ningu no em capturara'); }, 100);
});

// BE: promisificar (dormir.js del modul 2) i deixar que el rebuig arribi.
aplicacio.get('/be', async (peticio, resposta) => {
  await dormir(100);
  throw new Error('aquest si que arriba al gestor d\'errors');
});

  1. Factories de middleware

Un middleware fix serveix per a un cas. Una factoria —una funció que retorna un middleware— serveix per a tots, i és el patró que fan servir express.json({ limit }), helmet({ ... }) i cors({ ... }).

// src/middleware/exigir-capcalera.js
// Retorna un middleware que exigeix una capcalera, opcionalment amb patro.
function exigirCapcalera(nom, { patro } = {}) {
  // Tot el que es car es calcula UNA vegada, aqui, no a cada peticio.
  const nomNormalitzat = nom.toLowerCase();

  return function comprovarCapcalera(peticio, resposta, seguent) {
    const valor = peticio.headers[nomNormalitzat];
    if (!valor || (patro && !patro.test(valor))) {
      const missatge = `Capcalera ${nom} absent o invalida`;
      seguent(Object.assign(new Error(missatge), { codi: 'PARAMETRE_INVALID' }));
      return;
    }
    seguent();
  };
}

module.exports = { exigirCapcalera };

// Us: el mateix middleware, configurat de dues maneres diferents.
const patro = /^(web|taquilla|telefon)$/;
rutesComandes.post('/', exigirCapcalera('X-Canal-Venda', { patro }), crearComanda);
rutesSessions.use(exigirCapcalera('Accept'));

Tres avantatges concrets: és configurable sense duplicar codi; fa la feina cara una sola vegada (compilar regex, llegir configuració, obrir recursos), de manera que el middleware retornat només fa el mínim per petició; i és provable, perquè al mòdul 9 podràs cridar exigirCapcalera('X', {...}) i provar la funció retornada amb objectes falsos, sense aixecar cap servidor.

És exactament el mateix patró de crearRegistrePeticions(opcions) que ja has escrit a l'apartat 7. Quan dubtis entre exportar un middleware o una factoria, exporta la factoria: costa una línia més i t'estalvia una refactorització.

Errors Comuns i Consells

  • No cridar next() en algun camí. La petició es penja sense errors. Fes servir el detector de penjades en desenvolupament i escriu return després de cada seguent(...).
  • Cridar next() i a més respondre. Provoca Cannot set headers after they are sent; sol venir d'un return oblidat.
  • Registrar express.json() després de les rutes. req.body serà undefined: l'error número u dels primers POST.
  • Registrar el gestor d'errors al mig. Només captura el que s'ha registrat abans que ell; va sempre l'últim.
  • Fer servir req.url als registres dins d'un muntatge. Veuràs rutes sense el prefix: fes servir req.originalUrl.
  • Middleware globals que només calen en una ruta. senseCache a tota l'aplicació destrossa la memòria cau dels estàtics; munta cadascun a l'àmbit mínim.
  • Feina cara dins del middleware en comptes de a la factoria. Compilar una regex a cada petició és malbaratament pur.
  • Confiar en capçaleres del client sense validar. X-Id-Peticio ve de fora: acota longitud i caràcters.
  • Consell: un middleware ha de fer una cosa. Si el teu autentica, registra i comprimeix, en són tres, i tres és el nombre de llocs on buscaràs la fallada.

Exercicis

Exercici 1: capçalera de temps de servei

Escriu src/middleware/temps-servei.js amb una factoria crearTempsServei() que afegeixi la capçalera X-Temps-Servei amb els mil·lisegons que ha trigat la petició. Pista: no pots fixar capçaleres a res.on('finish') perquè ja s'han enviat; cal interceptar el moment just abans envoltant res.end.

Exercici 2: caçar la petició penjada

Crea una aplicació amb tres middleware, on el segon té un camí que no crida seguent(). Comprova amb curl que la petició es queda penjada, afegeix-hi el detector de l'apartat 3 i demostra que el problema apareix a stderr amb mètode i ruta.

Exercici 3: comparar express.static amb la teva versió

Serveix public/ amb express.static configurat amb maxAge: '1h' i etag: true. Amb curl -si comprova: que la primera petició retorna 200 amb ETag i Cache-Control, que repetint-la amb -H 'If-None-Match: <etag>' retorna 304 sense cos, i que demanar /public/../.env no surt del directori. Contrasta el resultat amb el que feia el teu estatics.js.

Solucions

Solució 1

// src/middleware/temps-servei.js
function crearTempsServei(capcalera = 'X-Temps-Servei') {
  return function tempsServei(peticio, resposta, seguent) {
    const inici = process.hrtime.bigint();
    // Envoltem res.end: es l'ultim instant en que encara es poden
    // fixar capcaleres, perque writeHead encara no s'ha executat.
    const fiOriginal = resposta.end;
    resposta.end = function (...parametres) {
      if (!resposta.headersSent) {
        const duracioMs = Number(process.hrtime.bigint() - inici) / 1e6;
        resposta.setHeader(capcalera, duracioMs.toFixed(1));
      }
      return fiOriginal.apply(this, parametres);
    };
    seguent();
  };
}

res.on('finish') no serveix perquè aleshores les capçaleres ja han viatjat per la xarxa: setHeader llançaria o seria ignorat. res.on('close') és encara pitjor, perquè es pot disparar amb la connexió ja tancada. Aquest embolcall d'end és la tècnica que fa servir morgan internament.

Solució 2

// penjada.js
const aplicacio = require('express')();
aplicacio.use(detectorDePenjades); // el de l'apartat 3, sempre el primer

// El culpable: nomes continua si hi ha capcalera; si no, no fa res.
aplicacio.use((peticio, resposta, seguent) => {
  if (peticio.get('X-Sala')) seguent();
});

aplicacio.get('/entrades', (p, r) => r.json({ sala: p.get('X-Sala') }));
aplicacio.listen(3000);

curl -s --max-time 5 localhost:3000/entrades es penja i stderr mostra [penjada] GET /entrades; afegint-hi -H 'X-Sala: Sala Boveda' respon {"sala":"Sala Boveda"}.

Solució 3

# 1. Primera peticio: 200 amb Cache-Control: public, max-age=3600 i ETag.
curl -si localhost:3000/estils.css | head -n 8
# 2. Repeticio condicional: 304 Not Modified, sense cos.
curl -si -H 'If-None-Match: W/"1a4-1949f2c0a10"' localhost:3000/estils.css | head -n 2
# 3. Intent de recorregut de directoris: 404, no surt del directori.
curl -si 'localhost:3000/../.env' | head -n 1

El tercer cas funciona perquè express.static normalitza la ruta i rebutja el que surti del directori base, igual que el teu resoldreDinsDe; la diferència és que ara aquesta protecció està en un paquet que milions de desplegaments exerciten cada dia, no en vint línies teves.

Conclusió

El middleware és el mecanisme únic sobre el qual està construït tot Express: una pila de funcions (req, res, next) recorreguda en l'ordre exacte de registre, on cadascuna llegeix, modifica, curtcircuita o passa el testimoni, i on next(error) obre un segon camí del qual no es torna. Has vist el diagrama complet del recorregut, l'error més frustrant del framework —no cridar next()— i com diagnosticar-lo, la diferència entre req.url, req.baseUrl i req.originalUrl en muntar amb prefix, i per què l'ordre de registre no admet excepcions.

Escena Viva ja té els seus middleware propis: idPeticio per a la traçabilitat, crearRegistrePeticions mesurant amb res.on('finish') i escrivint per stderr, senseCache muntat només on l'aforament canvia, i exigirCapcalera com a exemple de factoria configurable. I has comprovat que express.json() és el teu cos.js amb més casos límit coberts, i que express.static() és el teu estatics.js més tipus-mime.js amb suport de rangs i dotfiles. Tots són teus fins ara.

A la lliçó següent, Middleware de Tercers Essencials, muntem el kit mínim sense el qual cap API no hauria de sortir a producció: helmet i les seves capçaleres de seguretat una a una, cors perquè el public/app.js d'Escena Viva pugui cridar l'API des d'un altre origen, morgan per al registre HTTP, compression per a l'amplada de banda i express-rate-limit perquè un script no exhaureixi l'aforament de l'Auditorio Ribera en deu segons. Cadascun passat pel filtre d'auditoria que vas aprendre al mòdul 5, i tots ordenats a la versió definitiva de crearAplicacio().

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