Al mòdul 4 vas escriure un servidor HTTP complet sense ni una sola dependència: un enrutador amb patrons compilats a expressions regulars, un lector de cos amb límit de mida, un servidor de fitxers estàtics amb ETag i compressió, i una taula que traduïa els codis d'error del domini a estats HTTP. Va ser molta feina, i va ser feina ben invertida: ara saps exactament què fa un framework web per dins. En aquest mòdul arriba Express, i la teva experiència serà diferent de la del principiant que l'aprèn de zero. On d'altres veuen màgia, tu hi reconeixeràs peces que ja vas construir.

Aquesta primera lliçó respon a una pregunta molt concreta: quin problema resol Express exactament. No «per què és popular» ni «com es fa servir en general», sinó quines línies de codi desapareixen del teu projecte quan l'instal·les, i quines línies continuen sent teves per sempre.

Contingut

  1. Què és un framework web
  2. La comparació honesta: el mateix endpoint, dues vegades
  3. Què et dóna Express i què continua sent teu
  4. La filosofia minimalista i les seves conseqüències
  5. L'ecosistema: alternatives reals
  6. Instal·lar Express amb els ulls del mòdul 5
  7. Express 5 enfront d'Express 4
  8. El primer app.js d'Escena Viva
  9. Desmuntant la màgia: req i res són els de node:http
  10. Què és app en realitat

  1. Què és un framework web

Un framework web és una biblioteca que se situa entre el mòdul node:http i el teu codi de negoci, i que resol els problemes que apareixen sempre, en qualsevol servidor HTTP, sigui quin sigui el domini. Ningú no escriu una API sense necessitar:

  • Decidir quina funció atén cada combinació de mètode i ruta (enrutament).
  • Extreure paràmetres de la URL (/esdeveniments/evt-001 → id = 'evt-001').
  • Executar tasques comunes abans de cada gestor: registrar la petició, llegir el cos, comprovar capçaleres (middleware).
  • Serialitzar respostes JSON amb el Content-Type i la longitud correctes.
  • Servir fitxers estàtics.
  • Capturar errors en un punt únic i convertir-los en respostes coherents.

Tu vas resoldre aquests sis problemes a mà al mòdul 4. Un framework els resol per tu, i ho fa amb un codi que han revisat milers de persones durant més d'una dècada.

La paraula clau és transversal: el framework no sap res d'aforaments, sessions ni entrades. Això és teu. El framework sap d'HTTP.

  1. La comparació honesta: el mateix endpoint, dues vegades

Posarem de costat el mateix endpoint, GET /esdeveniments/:id, tal com va quedar al mòdul 4 i tal com queda amb Express. Sense trampes: el codi artesanal és el que vas escriure tu, i fa exactament el mateix.

Versió artesanal (mòdul 4)

// Extracte de src/servidor/rutes-api.js del modul 4 (resumit).
const { crearEnrutador } = require('./enrutador.js');
const { respondreJson, respondreError } = require('./respostes.js');
const { obtenirEsdevenimentPerId } = require('../cataleg-dades.js');

const enrutador = crearEnrutador();

// 1. Registre de la ruta: el patro es compila a expressio regular.
enrutador.registrar('GET', '/esdeveniments/:id', async (peticio, resposta, parametres) => {
  try {
    // 2. El parametre arriba en un objecte que va construir el meu aparellador.
    const esdeveniment = await obtenirEsdevenimentPerId(parametres.id);
    // 3. La serialitzacio, el Content-Type i el Content-Length son meus.
    respondreJson(resposta, 200, esdeveniment.toJSON());
  } catch (error) {
    // 4. La traduccio de codi de domini a estat HTTP es meva, i a mes
    //    m'he de recordar d'envoltar CADA gestor asincron en try/catch.
    respondreError(resposta, error);
  }
});

module.exports = { enrutador };

I això no compta les peces de què depèn: enrutador.js (compilar patrons, aparellar, gestionar el 405 amb Allow), respostes.js (quatre funcions d'escriptura) i errors-http.js (la taula d'estats). Entre les tres sumen uns quants centenars de línies que vas haver d'escriure, entendre i mantenir.

Versió amb Express

// src/rutes/esdeveniments.js amb Express 5.
const express = require('express');
const { obtenirEsdevenimentPerId } = require('../cataleg-dades.js');

const rutesEsdeveniments = express.Router();

rutesEsdeveniments.get('/:id', async (peticio, resposta) => {
  const esdeveniment = await obtenirEsdevenimentPerId(peticio.params.id);
  resposta.json(esdeveniment.toJSON());
});

module.exports = { rutesEsdeveniments };

Quatre línies de lògica enfront d'unes quinze, i sobretot zero línies d'infraestructura. Fixa't en què ha desaparegut concretament:

Peça del mòdul 4 Què passa amb Express
compilarPatro / aparellar d'enrutador.js Ho fa router.get('/:id') internament
Construir l'objecte parametres Arriba com a peticio.params
respondreJson (capçaleres + JSON.stringify) És resposta.json()
El try/catch de cada gestor async A Express 5 el rebuig va sol al gestor d'errors
El 405 amb capçalera Allow Express ho gestiona en recórrer les rutes
normalitzarRuta (barres finals, majúscules) Ajustos strict routing i case sensitive routing

L'important no és que siguin menys línies. És que les línies que queden parlen només d'Escena Viva: cercar un esdeveniment i retornar-lo. Tota la resta era soroll necessari que ara algú manté per tu.

  1. Què et dóna Express i què continua sent teu

Aquí és on molts cursos menteixen per omissió. Express resol un conjunt acotat de problemes, i hi ha una llista igual de llarga de coses que continuen sent responsabilitat teva.

Àrea Ho resol Express? Comentari
Enrutament i paràmetres Sí Nucli del framework
Cadena de middleware Sí Nucli del framework
Respostes (json, send, status, sendFile) Sí Substitueix el teu respostes.js
Fitxers estàtics amb ETag i memòria cau Sí (express.static) Substitueix estatics.js + tipus-mime.js
Lectura del cos amb límit Sí (express.json({ limit })) Substitueix cos.js, 413 inclòs
Punt únic de gestió d'errors Sí (middleware de 4 arguments) Però la política d'errors la defineixes tu
Validació de dades d'entrada No Lliçó 06-06, amb zod
Seguretat (capçaleres, CORS, límits) No Middleware de tercers, lliçó 06-05
Autenticació i autorització No Mòdul 8 sencer
Accés a dades / base de dades No Mòdul 7
Arquitectura de carpetes i capes No Lliçó 06-02: decideixes tu
Registre estructurat i observabilitat No Mòdul 11
Proves No Mòdul 9, amb supertest

Llegeix-ho dues vegades. Express t'estalvia la lampisteria d'HTTP. No t'estalvia pensar. Un servidor Express mal dissenyat és exactament igual de dolent que un servidor node:http mal dissenyat, només que més curt.

  1. La filosofia minimalista i les seves conseqüències

Express es descriu a si mateix com a fast, unopinionated, minimalist. La paraula que més importa és unopinionated: sense opinions. Express no et diu on posar les carpetes, com separar controladors de serveis, quin validador fer servir ni com estructurar els errors. Només et dóna el mecanisme.

Això té dues cares:

Avantatges

  • Pots adaptar l'estructura al teu projecte real, no a l'exemple de la documentació.
  • La superfície d'API que cal aprendre és petita: enrutament, middleware, req, res, i poca cosa més.
  • S'integra amb qualsevol cosa, perquè no imposa res.

Inconvenients

  • Dos projectes Express poden no assemblar-se en res, cosa que dificulta entrar en un codi aliè.
  • Has de prendre decisions que un framework amb opinions prendria per tu: validació, errors, capes, configuració.
  • És fàcil acabar amb un app.js de 800 línies si ningú no imposa disciplina.

Aquest mòdul va precisament d'això: prendre aquestes decisions bé i una sola vegada, i deixar-les escrites a l'estructura del projecte d'Escena Viva.

  1. L'ecosistema: alternatives reals

Express no és l'única opció, i convé saber què hi ha al costat per no defensar una eina per costum.

Framework Idea central Fort en A canvi
Express Middleware minimalista Ecosistema enorme, documentació i respostes per tot arreu, corba suau Poc opinat; la validació i els esquemes els poses tu
Fastify Rendiment i esquemes JSON Molt ràpid, validació i serialització per esquema integrades, connectors encapsulats Ecosistema menor, conceptes propis (encapsulació, hooks)
Koa Middleware amb async/await i context ctx Nucli minúscul i elegant, control fi del flux Gairebé tot és un paquet a part; menys material didàctic
NestJS Arquitectura amb opinions (decoradors, injecció de dependències) Projectes grans i equips nombrosos, estructura imposada Molt per aprendre abans de la primera ruta; TypeScript de facto
Hapi Configuració declarativa Validació i seguretat integrades, tot per configuració Estil molt propi, comunitat més petita

Per què aquest curs ensenya Express: perquè els conceptes que aprens aquí (middleware en cadena, enrutament, gestió centralitzada d'errors) són els mateixos que fan servir Koa, Fastify i NestJS amb una altra sintaxi, i perquè és el framework que més et trobaràs en codi existent. Aprendre Express és aprendre el vocabulari de l'ecosistema.

  1. Instal·lar Express amb els ulls del mòdul 5

L'instal·larem, però no amb els ulls tancats. Al mòdul 5 vas aprendre a auditar el que entra a node_modules; aplica-ho ara.

# Installacio com a dependencia de produccio.
npm install express

# Quina versio ha entrat exactament?
npm ls express

# Quants paquets hi ha ara en total a l'arbre?
npm ls --all --parseable | wc -l

# Llicencies i metadades del paquet.
npm view express version license repository.url

# Auditoria de seguretat despres de la installacio.
npm audit

Què hauries d'observar i com interpretar-ho:

  • Express no és una peça única: arrossega diverses desenes de dependències transitives (router, body-parser, send, serve-static, cookie, mime-types, finalhandler, qs…). No t'espantis: la majoria són mòduls diminuts del mateix equip, i molts són literalment les peces que vas escriure a mà (send és el teu estatics.js, mime-types és el teu tipus-mime.js, body-parser és el teu cos.js).
  • Llicència MIT, permissiva, sense obligacions per al teu projecte.
  • Express és un projecte de la OpenJS Foundation, amb governança pública. Això és exactament el tipus de senyal que vas aprendre a buscar: no depèn d'una sola persona.
  • Fixa la versió al package.json i respecta el package-lock.json: en desplegament s'instal·la amb npm ci, no amb npm install.

Després de la instal·lació, el teu package.json inclourà una cosa com:

{
  "dependencies": {
    "dotenv": "^17.0.0",
    "express": "^5.1.0"
  }
}

  1. Express 5 enfront d'Express 4

Express 5 és la versió estable actual, i hi ha molt codi i molts tutorials escrits per a la 4. Aquestes són les diferències que de debò t'afectaran:

Aspecte Express 4 Express 5
Gestors async que rebutgen Es quedaven penjats tret que envoltessis el gestor El rebuig passa automàticament al gestor d'errors
req.query Objecte mutable, es podia reassignar Getter de només lectura: req.query = ... llança error
Comodins /fitxers/* amb req.params[0] /fitxers/*ruta amb req.params.ruta (nom obligatori)
Paràmetres opcionals /esdeveniments/:id? /esdeveniments{/:id} (sintaxi de grup opcional)
body-parser Paquet a part per a alguns casos express.json() i express.urlencoded() integrats
res.status(...) amb codi invàlid S'acceptava Llança error si no és un enter de 100 a 999

Les dues primeres files són les que trenquen més codi antic. La primera és una millora enorme que veurem a fons a 06-07; la segona provoca un error molt concret quan algú intenta «netejar» la query reassignant-la, i el tractarem a 06-06.

  1. El primer app.js d'Escena Viva

Deu línies útils. Crea'l com a app-minim.js a l'arrel només per a aquesta prova; a 06-02 l'estructurarem de debò.

// app-minim.js
const express = require('express');

const aplicacio = express();

// Una ruta de salut, igual que la GET /salut del modul 4.
aplicacio.get('/salut', (peticio, resposta) => {
  resposta.json({ estat: 'ok', instant: new Date().toISOString() });
});

// Un endpoint amb parametre de ruta.
aplicacio.get('/esdeveniments/:id', (peticio, resposta) => {
  resposta.json({ id: peticio.params.id, sala: 'Teatro Almendra' });
});

aplicacio.listen(3000, () => {
  // Diagnostics per stderr, com marca la convencio del curs.
  console.error('Escena Viva escoltant a http://localhost:3000');
});
node app-minim.js
curl -s http://localhost:3000/salut
curl -s http://localhost:3000/esdeveniments/evt-001
curl -si http://localhost:3000/ruta-que-no-existeix | head -n 1

Repassa el resultat amb atenció:

  • GET /salut retorna JSON amb Content-Type: application/json; charset=utf-8 sense que tu hagis posat la capçalera. Això ho fa resposta.json().
  • GET /esdeveniments/evt-001 et retorna el paràmetre extret. Això ho feia el teu compilarPatro.
  • Una ruta inexistent retorna 404 Not Found amb un cos HTML per defecte. A 06-07 el substituirem pel nostre format { error: { codi, missatge, estat } }.

  1. Desmuntant la màgia: req i res són els de node:http

Aquesta és la idea més important de la lliçó. Els objectes req i res d'Express no són objectes nous: són exactament l'IncomingMessage i el ServerResponse de node:http que ja coneixes del mòdul 4, amb mètodes afegits al seu prototip. Ho demostrarem.

// app-demostracio.js
const http = require('node:http');
const express = require('express');

const aplicacio = express();

aplicacio.get('/prova', (peticio, resposta) => {
  // 1. Son instancies de les classes del nucli de Node.
  const esPeticioDelNucli = peticio instanceof http.IncomingMessage;
  const esRespostaDelNucli = resposta instanceof http.ServerResponse;

  // 2. Els metodes de node:http continuen aqui i funcionen.
  resposta.setHeader('X-Escena-Viva', 'Teatro Almendra');
  resposta.statusCode = 200;

  // 3. I la peticio continua sent un stream llegible del modul 3.
  const esStream = typeof peticio.on === 'function' && typeof peticio.pipe === 'function';

  // 4. Metode d'Express, que per dins fa servir els tres de dalt.
  resposta.json({ esPeticioDelNucli, esRespostaDelNucli, esStream });
});

aplicacio.listen(3000);
curl -si http://localhost:3000/prova

Resposta:

HTTP/1.1 200 OK
X-Escena-Viva: Teatro Almendra
Content-Type: application/json; charset=utf-8

{"esPeticioDelNucli":true,"esRespostaDelNucli":true,"esStream":true}

Conseqüències pràctiques d'això, que et serviran durant tot el mòdul:

  • Tot el que vas aprendre al mòdul 4 sobre res.writeHead, res.end, req.headers o req.method continua sent vàlid dins d'Express.
  • resposta.json(objecte) és aproximadament: fixar Content-Type, serialitzar amb JSON.stringify i cridar end(). És el teu respondreJson, amb més casos límit coberts (ETag, json spaces, caràcters especials).
  • Com que req continua sent un stream, el pots llegir amb pipeline per a pujades grans en comptes d'acumular-lo a memòria.
  • Si alguna cosa d'Express no fa el que vols, sempre pots baixar un pis a l'API de node:http. No hi ha cap caixa negra; hi ha una capa fina.

  1. Què és app en realitat

Segona desmitificació. express() retorna una funció. Concretament, una funció amb la signatura (req, res, next) a la qual, a més, se li han penjat propietats i mètodes (get, post, use, set, listen…). I aquesta signatura et resultarà familiar: és la d'un gestor de http.createServer.

const http = require('node:http');
const express = require('express');

const aplicacio = express();
aplicacio.get('/salut', (peticio, resposta) => resposta.json({ estat: 'ok' }));

// La prova: app es una funcio.
console.error('Tipus d\'app:', typeof aplicacio); // 'function'

// I per aixo se li pot passar directament a createServer.
const servidor = http.createServer(aplicacio);
servidor.listen(3000, () => console.error('Servidor propi amb app d\'Express'));

aplicacio.listen(3000) és només una drecera que fa internament http.createServer(this).listen(3000). Saber això no és anecdòtic: a 06-02 crearem el servidor a mà precisament per poder tancar-lo de forma ordenada, i al mòdul 12 ho necessitarem per acoblar Socket.IO al mateix servidor.

Dit d'una altra manera: Express és una funció gestora molt sofisticada. Tota la màgia del framework consisteix que, quan aquesta funció s'executa, recorre una llista de middleware i rutes registrades i va cridant les que encaixen. Res més. A 06-04 ho veurem amb un diagrama.

Errors Comuns i Consells

  • Creure que Express «és» el backend. Express és una capa de transport HTTP. La teva lògica de negoci (les classes Esdeveniment, Sessio, GestorDeVendes) no ha de saber que Express existeix. Si un dia canvies a Fastify, el domini no es toca.
  • Seguir tutorials d'Express 4 amb Express 5 instal·lat. Els símptomes típics: rutes amb :id? que deixen de funcionar, * sense nom que llança error en arrencar, i codi que reassigna req.query i falla. Comprova sempre npm ls express.
  • Instal·lar sense mirar. Aplica el criteri del mòdul 5 a cada paquet, també als de la lliçó 06-05. Express és fiable; no tots els middleware de tercers ho són.
  • Envoltar-ho tot en try/catch per costum. A Express 5 no cal per al cas general; només quan vulguis convertir l'error en un altre de més específic abans de propagar-lo.
  • Oblidar que app és una funció. Quan passi alguna cosa estranya amb l'arrencada, recorda que pots construir el servidor tu i depurar al nivell de node:http.
  • Consell: conserva el codi del mòdul 4 al repositori. Durant tot aquest mòdul el compararem amb el nou, i aquesta comparació és la que converteix Express en una cosa que entens, no en una cosa que copies.

Exercicis

Exercici 1: auditoria de la instal·lació

Instal·la Express al projecte d'Escena Viva i respon per escrit: quina versió exacta ha entrat, quina llicència té, quants paquets hi ha a l'arbre de dependències abans i després, i quines d'aquestes dependències transitives es corresponen amb fitxers que vas escriure a mà al mòdul 4.

Exercici 2: la ruta de salut, dues vegades

Escriu dos fitxers que exposin el mateix endpoint GET /salut retornant { estat: 'ok', sala: 'Sala Boveda' }: un amb node:http pur (sense fer servir el teu enrutador del mòdul 4, tot al gestor) i un altre amb Express. Compta les línies de cadascun i anota què vas haver de fer a mà al primer.

Exercici 3: demostrar el parentiu

Crea un endpoint GET /diagnostic a Express que retorni un JSON amb: el nom de la classe de req (peticio.constructor.name), el nom de la classe de res, la llista de capçaleres que ja ha fixat la resposta abans d'enviar, i el valor de peticio.httpVersion. Afegeix-hi també una capçalera personalitzada fent servir setHeader per comprovar que l'API de node:http continua disponible.

Solucions

Solució 1

npm install express
npm ls express                       # p. ex. [email protected]
npm view express license             # MIT
npm ls --all --parseable | wc -l     # comparar abans i despres

Correspondències amb el teu codi del mòdul 4:

Dependència transitiva Fitxer teu del mòdul 4
body-parser src/servidor/cos.js
serve-static i send src/servidor/estatics.js
mime-types src/servidor/tipus-mime.js
router / path-to-regexp src/servidor/enrutador.js
finalhandler El 404/500 final de gestionarPeticio

Solució 2

// salut-http.js — amb node:http pur.
const http = require('node:http');

const servidor = http.createServer((peticio, resposta) => {
  const url = new URL(peticio.url, `http://${peticio.headers.host}`);
  if (peticio.method !== 'GET') {
    resposta.writeHead(405, { Allow: 'GET', 'Content-Type': 'application/json' });
    resposta.end(JSON.stringify({ error: { codi: 'METODE_NO_PERMES', estat: 405 } }));
    return;
  }
  if (url.pathname !== '/salut') {
    resposta.writeHead(404, { 'Content-Type': 'application/json' });
    resposta.end(JSON.stringify({ error: { codi: 'RUTA_NO_TROBADA', estat: 404 } }));
    return;
  }
  const cos = JSON.stringify({ estat: 'ok', sala: 'Sala Boveda' });
  resposta.writeHead(200, {
    'Content-Type': 'application/json; charset=utf-8',
    'Content-Length': Buffer.byteLength(cos),
  });
  resposta.end(cos);
});

servidor.listen(3000);
// salut-express.js — amb Express.
const express = require('express');

const aplicacio = express();
aplicacio.get('/salut', (peticio, resposta) => {
  resposta.json({ estat: 'ok', sala: 'Sala Boveda' });
});
aplicacio.listen(3000);

A mà vas haver de: analitzar la URL, comprovar el mètode, generar el 405 amb Allow, generar el 404, serialitzar, calcular Content-Length i escriure les capçaleres. Express fa les set coses.

Solució 3

// diagnostic.js
const express = require('express');

const aplicacio = express();

aplicacio.get('/diagnostic', (peticio, resposta) => {
  resposta.setHeader('X-Escena-Viva-Sala', 'Auditorio Ribera');
  resposta.json({
    classePeticio: peticio.constructor.name,        // IncomingMessage
    classeResposta: resposta.constructor.name,      // ServerResponse
    capcaleresJaFixades: resposta.getHeaderNames(), // inclou x-escena-viva-sala
    versioHttp: peticio.httpVersion,                // '1.1'
  });
});

aplicacio.listen(3000, () => console.error('Diagnostic al :3000'));

El rellevant de la resposta: els noms de classe són els del nucli de Node, no classes pròpies d'Express, i getHeaderNames() és un mètode de ServerResponse que continua estant disponible.

Conclusió

Express no és màgia: és l'abstracció de les sis tasques transversals de tot servidor HTTP —enrutament, paràmetres, middleware, respostes, estàtics i errors—, resoltes per tu a mà al mòdul 4 i ara delegades a un codi madur i auditat. Has vist la comparació línia a línia, la llista honesta del que Express no fa per tu (validació, seguretat, arquitectura, dades, proves), les alternatives de l'ecosistema, i les dues idees que desmunten l'aparent màgia: req i res són els objectes de node:http amb mètodes afegits, i app és simplement una funció gestora que es pot passar a http.createServer.

També saps ja en què es diferencia Express 5 d'Express 4 i per què aquestes diferències t'estalviaran codi a 06-07.

El que tens ara mateix és un app.js de joguina: deu línies a l'arrel del projecte. Això no és una aplicació mantenible. A la lliçó següent, Configuració d'una Aplicació Express, farem el salt: separarem crearAplicacio() de la posada en marxa del servidor —una separació que resulta obligatòria per poder provar amb supertest al mòdul 9 i per a l'aturada ordenada del mòdul 11—, definirem l'estructura de carpetes d'Escena Viva, configurarem els ajustos de l'aplicació (trust proxy, x-powered-by, strict routing) i centralitzarem la configuració per entorn en un mòdul que falli ràpid si hi falta alguna cosa.

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