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
- Què és un middleware
- El recorregut d'una petició
next(),next(error)i no cridarnext()- Tipus de middleware
- L'ordre de registre és l'ordre d'execució
- Middleware amb ruta de muntatge
- Els middleware propis d'Escena Viva
- Middleware integrats:
json,urlencoded,static - Middleware asíncron
- Factories de middleware
- 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 rutaLa 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.
- 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.
next(), next(error) i no cridar next()
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(...), escriureturntret que sigui l'última línia. Cridarseguent()dues vegades provoca l'avísCannot set headers after they are sent to the client, un altre clàssic difícil de rastrejar.
- 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).
- 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.
- 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.jsfes servir semprereq.originalUrl. Ambreq.urldins d'un muntatge, els teus logs mostraran rutes incompletes i no les podràs correlacionar amb el que veu el client.
- 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 cachejarQue 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 }).
- 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.
- 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');
});
- 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 escriureturndesprés de cadaseguent(...). - Cridar
next()i a més respondre. ProvocaCannot set headers after they are sent; sol venir d'unreturnoblidat. - Registrar
express.json()després de les rutes.req.bodyseràundefined: l'error número u dels primersPOST. - 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.urlals registres dins d'un muntatge. Veuràs rutes sense el prefix: fes servirreq.originalUrl. - Middleware globals que només calen en una ruta.
senseCachea 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-Peticiove 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 1El 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
- Què és Node.js?
- Instal·lació i Configuració de l'Entorn
- El Teu Primer Programa en Node.js
- El REPL de Node.js
- JavaScript Modern per a Node.js
- El Projecte del Curs: la Plataforma Escena Viva
Mòdul 2: Conceptes Bàsics
- Arquitectura de Node.js
- El Bucle d'Esdeveniments (Event Loop)
- Callbacks i Programació Asíncrona
- Promeses i async/await
- Esdeveniments i EventEmitter
- Mòduls CommonJS i require()
- Mòduls ES i Interoperabilitat
Mòdul 3: Sistema de Fitxers i E/S
- Lectura i Escriptura de Fitxers
- El Mòdul fs a Fons
- Rutes Multiplataforma amb el Mòdul path
- Treballant amb Streams
- Streams de Transformació i pipeline
- Buffers i Dades Binàries
Mòdul 4: HTTP i Servidors Web
- Creant un Servidor HTTP Simple
- Gestió de Sol·licituds i Respostes
- Enrutament Manual
- Servint Fitxers Estàtics
- Rebent Dades: Cossos de Petició i JSON
- Consumint APIs Externes des de Node.js
Mòdul 5: NPM i Gestió de Paquets
- Introducció a NPM i package.json
- Instal·lació i Ús de Paquets
- Versionat Semàntic i package-lock
- Scripts d'npm i Automatització del Projecte
- Creació i Publicació de Paquets
- Seguretat i Manteniment de Dependències
Mòdul 6: Framework Express.js
- Introducció a Express.js
- Configuració d'una Aplicació Express
- Enrutament a Express
- Middleware
- Middleware de Tercers Essencials
- Validació de Dades d'Entrada
- Gestió d'Errors
Mòdul 7: Bases de Dades i ORMs
- Introducció a les Bases de Dades
- Usant MongoDB amb Mongoose
- Operacions CRUD
- Relacions, Poblat i Consultes Avançades
- Usant Bases de Dades SQL amb Sequelize
- Migracions, Transaccions i Dades de Prova
Mòdul 8: Autenticació i Autorització
- Introducció a l'Autenticació
- Registre d'Usuaris i Hash de Contrasenyes
- Sessions i Galetes amb Passport.js
- Autenticació amb JWT
- Control d'Accés Basat en Rols
- Bones Pràctiques de Seguretat en APIs
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries amb Mocha i Chai
- Dobles de Prova amb Sinon
- Proves d'Integració
- Cobertura i Automatització de les Proves
- Depuració d'Aplicacions Node.js
Mòdul 10: Temes Avançats
- El Mòdul Cluster
- Fils de Treball (Worker Threads)
- Memòria Cau i Cues de Treball amb Redis
- Optimització del Rendiment
- Construcció d'APIs RESTful
- GraphQL amb Node.js
Mòdul 11: Desplegament i DevOps
- Configuració i Variables d'Entorn
- Registre i Monitoratge en Producció
- Usant PM2 per a la Gestió de Processos
- Empaquetatge amb Docker
- Desplegant a Heroku i Altres PaaS
- Integració i Desplegament Continus
