Tancàvem el Mòdul 3 amb una frase: el projecte sap llegir i escriure, però no sap parlar amb ningú. Escena Viva té un catàleg al disc, informes per mes, rutes blindades i canonades que processen vendes amb memòria constant. Tot això ho consumeix una única persona: qui executa l'ordre a la seva terminal.
Avui això canvia. En acabar aquesta lliçó, qualsevol navegador de la teva xarxa podrà demanar dades a Escena Viva.
I veuràs de seguida que no som en territori desconegut. Un servidor HTTP de Node és un EventEmitter que emet request —els mateixos on i emit de la lliçó 02-05—, arrencar-lo és una operació asíncrona que es confirma amb un esdeveniment, i mantenir el procés viu és exactament el mecanisme de compte de referències del bucle d'esdeveniments que vam estudiar a 02-01. El mòdul node:http no afegeix conceptes nous: connecta a la xarxa els que ja tens.
Contingut
- HTTP en cinc minuts: petició, resposta i un intercanvi real
http.createServer: el servidor és unEventEmitterserver.listen, l'esdevenimentlisteningi el port- El primer servidor d'Escena Viva
- Tres maneres de provar-lo: navegador,
curlinode -e - Per què el procés ja no acaba tot sol
- Errors d'arrencada:
EADDRINUSEiEACCES - Aturada ordenada:
server.close()iSIGINT
- HTTP en cinc minuts: petició, resposta i un intercanvi real
HTTP és un protocol de petició i resposta sobre text. El client obre una connexió TCP, envia un bloc de text amb un format molt concret, i el servidor contesta amb un altre bloc. Res més. Ni el client ni el servidor conserven memòria del que ha passat entre una petició i l'altra: HTTP és sense estat, i tot el que sembla estat (sessions, cistelles, usuaris connectats) es construeix a sobre, com veurem al Mòdul 8.
Una petició té quatre parts i una resposta tres:
| Missatge | Part | Exemple | Què significa |
|---|---|---|---|
| Petició | Mètode | GET, POST, PUT, DELETE, HEAD |
La intenció: llegir, crear, reemplaçar, esborrar |
| Petició | Ruta | /esdeveniments/evt-001?format=json |
Quin recurs, amb paràmetres de consulta opcionals |
| Petició | Capçaleres | Accept: application/json |
Metadades: què accepto, qui sóc, què envio |
| Petició | Cos | {"sessioId":"ses-001-1"} |
Dades. Opcional; GET normalment no en porta |
| Resposta | Codi d'estat | 200, 404, 500 |
Com ha anat, en un nombre de tres xifres |
| Resposta | Capçaleres | Content-Type: application/json |
Metadades sobre el que torno |
| Resposta | Cos | El JSON, l'HTML, els bytes del cartell | El contingut. Opcional (un 204 no en porta) |
Així es veu un intercanvi complet, tal com viatja pel cable. La línia en blanc és el que separa les capçaleres del cos, i és obligatòria:
GET /esdeveniments HTTP/1.1
Host: localhost:3000
User-Agent: curl/8.5.0
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 69
Date: Tue, 14 Aug 2026 09:12:30 GMT
Connection: keep-alive
{
"esdeveniments": 3,
"sessions": 7,
"entradesLliures": 1189
}Fixa't en dos detalls que t'estalviaran hores de depuració més endavant:
- La ruta que arriba al servidor no inclou el domini.
Host: localhost:3000viatja en una capçalera a part. Per això, com veurem a la lliçó següent,req.urlval/esdevenimentsi maihttp://localhost:3000/esdeveniments. - Les capçaleres són text pla
Nom: valor, insensibles a majúscules, i es poden repetir. Node te les lliurarà sempre normalitzades a minúscules.
La bona notícia és que no hauràs d'escriure aquest text a mà: el mòdul node:http l'analitza en rebre'l i el genera en respondre. Tu treballes amb dos objectes, req i res.
http.createServer: el servidor és un EventEmitter
http.createServer: el servidor és un EventEmitterEl servidor mínim cap en quatre línies:
const http = require('node:http');
const servidor = http.createServer((req, res) => {
res.end("Hola des d'Escena Viva\n");
});
servidor.listen(3000);Desa'l com a hola.js, executa'l amb node hola.js i obre http://localhost:3000. Ja tens un servidor web.
Ara el que importa: aquesta funció que passes a createServer no té res d'especial. http.Server hereta de net.Server, que hereta d'EventEmitter. Passar el gestor al constructor és pur sucre sintàctic: Node fa un servidor.on('request', gestor) per tu. Aquestes dues versions són exactament equivalents:
// Versio A: el gestor com a argument (l'habitual)
const servidor = http.createServer((req, res) => {
res.end('hola');
});
// Versio B: registrant l'oient a ma (identica a l'anterior)
const servidor = http.createServer();
servidor.on('request', (req, res) => {
res.end('hola');
});Saber-ho no és anecdòtic: vol dir que pots registrar diversos oients de request, i que tot el que vas aprendre sobre EventEmitter s'aplica aquí. Un cas real és separar el registre de peticions de la lògica:
// S'executa abans que l'altre: els oients s'invoquen en ordre de registre.
servidor.on('request', (req) => console.error(`[peticio] ${req.method} ${req.url}`));
servidor.on('request', gestionarPeticio); // La logica de veritatEl diagnòstic va a stderr i les dades a stdout, com fem des del Mòdul 1.
Aquests són els esdeveniments del servidor que importen en aquest mòdul:
| Esdeveniment | Quan s'emet | Arguments |
|---|---|---|
request |
Arriba una petició HTTP completa (capçaleres; el cos encara pot estar viatjant) | (req, res) |
listening |
El sòcol ja accepta connexions | — |
connection |
S'obre una connexió TCP, abans de cap petició | (socket) |
close |
El servidor ha deixat d'acceptar i ja no queden connexions | — |
error |
Fallada del servidor, típicament en arrencar | (error) |
clientError |
Un client ha enviat alguna cosa que no és HTTP vàlid | (error, socket) |
La diferència entre connection i request mereix un moment. No són el mateix: HTTP/1.1 manté la connexió oberta per defecte (Connection: keep-alive), de manera que un navegador que carrega una pàgina amb tres imatges pot obrir una connexió i enviar-hi quatre peticions. Veure-ho en directe aclareix el concepte:
let connexions = 0;
let peticions = 0;
servidor.on('connection', () => console.error(`[tcp] connexions: ${++connexions}`));
servidor.on('request', () => console.error(`[http] peticions: ${++peticions}`));Recarrega la pàgina un parell de vegades i veuràs com el comptador de peticions puja molt més de pressa que el de connexions. Aquest detall torna a l'apartat 8, quan intentem aturar el servidor.
server.listen, l'esdeveniment listening i el port
server.listen, l'esdeveniment listening i el portlisten és el que posa el servidor a escoltar. La seva signatura habitual és listen(port, host, callback), i és asíncrona: quan la crida retorna, el sòcol encara no està a punt.
Aquest callback no és error-first: és simplement un oient de listening registrat amb once. Per això aquesta versió fa el mateix:
servidor.on('listening', () => {
const { address, port } = servidor.address();
console.error(`Escoltant a http://${address}:${port}`);
});
servidor.listen(3000, '127.0.0.1');servidor.address() torna { address, family, port } i només té valor després de listening; abans torna null. És la manera correcta d'esbrinar el port real quan demanes el port 0, que vol dir "el sistema operatiu en tria un de lliure" —un truc imprescindible a les proves automàtiques del Mòdul 9, on no pots fixar un port sense arriscar-te a col·lisions.
El segon argument, el host, decideix qui s'hi pot connectar: '127.0.0.1' accepta només connexions des d'aquesta mateixa màquina, i '0.0.0.0' (el valor per defecte) les accepta per totes les interfícies de xarxa, que és el que cal en contenidors o per provar des del mòbil.
En desenvolupament, 127.0.0.1 és l'opció prudent: evita que el teu servidor a mig fer quedi exposat a la xarxa de la cafeteria. A Docker, en canvi, és un error clàssic, perquè el contenidor no rebria el trànsit redirigit des de fora (ho veurem al Mòdul 11).
- El primer servidor d'Escena Viva
Anem amb el codi real. Crea src/servidor/servidor.js. La regla que ens autoimposem des del minut u és la que defensem tot el curs: res d'E/S síncrona dins del gestor. El catàleg es llegeix amb obtenirCataleg(), que és asíncron des de la lliçó 03-01.
// src/servidor/servidor.js
// Primer servidor HTTP d'Escena Viva: respon un resum del cataleg.
const http = require('node:http');
const { obtenirCataleg } = require('../cataleg-dades.js');
const PORT = Number(process.env.PORT) || 3000;
const HOST = process.env.HOST || '127.0.0.1';
// El gestor es ASINCRON: a dins hi ha una lectura de disc.
async function gestionarPeticio(req, res) {
const esdeveniments = await obtenirCataleg();
const resum = {
esdeveniments: esdeveniments.length,
sessions: esdeveniments.reduce((total, esdeveniment) => total + esdeveniment.nombreSessions, 0),
aforamentTotal: esdeveniments.reduce((total, esdeveniment) => total + esdeveniment.aforamentTotal, 0),
entradesVenudes: esdeveniments.reduce((total, esdeveniment) => total + esdeveniment.entradesVenudes, 0)
};
resum.entradesLliures = resum.aforamentTotal - resum.entradesVenudes;
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify(resum, null, 2));
}
function crearServidor() {
// El gestor es async: torna una promesa que NINGU no recull. Si falla,
// seria un unhandledRejection (llico 02-04), aixi que es captura aqui.
const servidor = http.createServer((req, res) => {
gestionarPeticio(req, res).catch((error) => {
console.error('[error] fallada no controlada:', error);
if (!res.headersSent) {
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
}
res.end(JSON.stringify({ error: 'Error intern del servidor' }, null, 2));
});
});
return servidor;
}
function principal() {
const servidor = crearServidor();
servidor.listen(PORT, HOST, () => {
const { address, port } = servidor.address();
console.error(`[servidor] escoltant a http://${address}:${port}`);
});
}
if (require.main === module) principal();
module.exports = { crearServidor, gestionarPeticio, PORT, HOST };Quatre decisions que ens acompanyaran durant tot el mòdul:
crearServidor()s'exporta sense arrencar-lo. Separar la construcció de l'arrencada és el que permetrà aixecar el servidor en un port aleatori dins d'una prova (Mòdul 9) sense que el fitxer obri un port pel simple fet de ser importat. Elrequire.main === moduleés el que fa que continuï sent executable ambnode src/servidor/servidor.js.- El
.catchno és opcional.http.createServerignora completament el valor que torni el gestor. Si el gestor ésasynci llança, la promesa queda rebutjada sense amo: el client es queda penjat fins que expiri el seu temps límit i, des de Node 15, el procés cau perunhandledRejection. Un sol esdeveniment corrupte al JSON tombaria el servidor sencer. res.headersSentevita l'errorERR_HTTP_HEADERS_SENT: si la fallada s'ha produït després de començar a escriure la resposta, ja no es poden canviar les capçaleres. És l'error més comú del mòdul i el desgranem a 04-02.- El port surt de l'entorn.
process.env.PORTpermet canviar-lo sense tocar el codi; en producció l'imposa la plataforma. La configuració per entorn és el tema de la lliçó 11-01.
- Tres maneres de provar-lo: navegador,
curl i node -e
curl i node -eArrenca el servidor:
El navegador és la prova més ràpida (http://localhost:3000), però també la que menys t'ensenya: no veus capçaleres ni codi d'estat, i afegeix peticions fantasma com /favicon.ico que t'embruten els registres. És útil, però no n'hi ha prou.
curl -i http://localhost:3000/ mostra la resposta completa, capçaleres incloses. És l'eina que farem servir a tot el mòdul:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Transfer-Encoding: chunked
{
"esdeveniments": 3,
"sessions": 7,
"aforamentTotal": 3000,
"entradesVenudes": 1811,
"entradesLliures": 1189
}Aquí hi ha els números de la llavor: 3 esdeveniments, 7 sessions, aforament 3000, 1811 venudes i 1189 lliures. Fixa't que no hi ha Content-Length sinó Transfer-Encoding: chunked; a 04-02 explicarem exactament per què i quan convé cadascun.
Indicadors de curl que farem servir: -i inclou les capçaleres, -s silencia la barra de progrés, -X força el mètode, -H afegeix una capçalera i -d envia un cos.
I node -e, per provar des del mateix Node amb fetch (l'API client que estudiarem a fons a 04-06):
node -e "fetch('http://localhost:3000/').then(r => r.json()).then(d => console.log(d.entradesLliures))"
# 1189
- Per què el procés ja no acaba tot sol
Executa el servidor i fixa't en una cosa que no havia passat mai en aquest curs: el procés no acaba. Tots els scripts anteriors feien la seva feina i tornaven el control a la terminal. Aquest es queda allà, aparentment sense fer res.
No és màgia, és el compte de referències del bucle d'esdeveniments de la lliçó 02-01. Node surt quan no queden operacions pendents que puguin produir feina futura. Un sòcol a l'escolta és exactament això: un gestor actiu que pot despertar el bucle en qualsevol moment. Mentre existeixi, el compte no arriba a zero i el procés continua viu.
Ho pots comprovar desactivant aquesta referència:
const servidor = http.createServer((req, res) => res.end('hola'));
servidor.listen(3000, () => console.error('escoltant'));
servidor.unref(); // "Si NOMES queda aixo, no em mantinguis viu"Aquest programa imprimeix escoltant i acaba immediatament, sense haver servit res. El servidor estava correctament arrencat; simplement havia deixat de comptar. unref() té usos legítims (un servidor de mètriques secundari que no ha d'impedir el tancament), però per al servidor principal és una errada garantida. ref() desfà l'efecte.
La conseqüència pràctica és que, a partir d'ara, el teu procés té cicle de vida: arrenca, serveix durant dies i ha de poder aturar-se bé. D'això tracta l'apartat 8.
- Errors d'arrencada:
EADDRINUSE i EACCES
EADDRINUSE i EACCESArrenca el servidor en una terminal i, sense parar-lo, arrenca'l en una altra:
node:events:496
throw er; // Unhandled 'error' event
Error: listen EADDRINUSE: address already in use 127.0.0.1:3000Reconeix aquest missatge: Unhandled 'error' event és el de la lliçó 02-05. Un EventEmitter que emet error sense oients llança l'excepció i tomba el procés. http.Server no és una excepció a la regla.
Les dues fallades d'arrencada que veuràs a la pràctica:
| Codi | Causa | Solució |
|---|---|---|
EADDRINUSE |
El port ja està ocupat per un altre procés (sovint, el teu servidor anterior) | Tancar l'altre procés, o fer servir un altre port |
EACCES |
Els ports per sota de 1024 requereixen privilegis d'administrador a Linux i macOS | Fer servir un port alt (3000, 8080) i posar un proxy invers al davant en producció |
EACCES sorprèn qui intenta escoltar al port 80 "perquè és el d'HTTP". En sistemes tipus Unix, els ports inferiors a 1024 són privilegis reservats: només root els pot obrir. La resposta correcta mai no és executar Node com a root, sinó escoltar en un port alt i deixar que Nginx o el balancejador de la plataforma escoltin al 80/443 (Mòdul 11).
L'arranjament consisteix a registrar un oient d'error abans de cridar listen, i donar un missatge que s'entengui:
servidor.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
console.error(`[servidor] el port ${PORT} ja esta en us. Prova: PORT=3001 node ${__filename}`);
} else if (error.code === 'EACCES') {
console.error(`[servidor] sense permisos per al port ${PORT}. Fes servir un port >= 1024.`);
} else {
console.error('[servidor] error inesperat:', error);
}
process.exitCode = 1;
});error.code (en anglès, propietat del sistema) és el que arriba de libuv; no el confonguis amb el nostre error.codi de domini, que traduirem a estats HTTP a la lliçó següent. I process.exitCode = 1 en comptes de process.exit(1): deixa que el procés acabi de manera natural buidant les seves memòries intermèdies de sortida, com ja vam veure al Mòdul 1.
Per localitzar el culpable d'un EADDRINUSE a Linux o macOS: lsof -i :3000 et diu quin procés té el port, i kill <PID> l'allibera.
- Aturada ordenada:
server.close() i SIGINT
server.close() i SIGINTQuan prems Ctrl+C, la teva terminal envia el senyal SIGINT al procés, i el comportament per defecte de Node és morir a l'acte. Això és acceptable en un script; en un servidor, significa tallar a mitja frase les peticions en curs: un client es queda sense la seva resposta, i una escriptura de fitxer pot quedar a mitges.
L'aturada ordenada consisteix a deixar d'acceptar connexions noves i esperar que acabin les vives:
function installarAturadaOrdenada(servidor) {
let aturant = false;
function aturar(senyal) {
if (aturant) return; // Un segon Ctrl+C no ha de reentrar aqui
aturant = true;
console.error(`\n[servidor] rebut ${senyal}, tancant...`);
// 1. Deixa d'acceptar connexions NOVES. El callback es crida
// quan l'ultima connexio viva s'ha tancat.
servidor.close((error) => {
if (error) process.exitCode = 1;
console.error(error ? `[servidor] error en tancar: ${error.message}` : '[servidor] tancat netament');
});
// 2. Tanca les connexions ocioses keep-alive, que si no
// mantindrien el proces viu fins al seu temps limit.
servidor.closeIdleConnections();
// 3. Xarxa de seguretat: si en 10 s no ha tancat, forcar.
const termini = setTimeout(() => {
console.error('[servidor] termini exhaurit, tancament forcat');
servidor.closeAllConnections();
process.exit(1);
}, 10_000);
termini.unref(); // Aquest temporitzador no ha de mantenir viu el proces
}
process.on('SIGINT', () => aturar('SIGINT'));
process.on('SIGTERM', () => aturar('SIGTERM'));
}El punt delicat és el 2, i ve del que vam veure a l'apartat 2: close() no tanca les connexions existents, només el sòcol a l'escolta. Amb keep-alive, un navegador que t'ha demanat una pàgina deixa la seva connexió TCP oberta uns segons per si demana res més. Aquestes connexions ocioses no serveixen res, però compten com a vives, així que close() es queda esperant i sembla que el teu servidor "no tanca". closeIdleConnections() (Node 18.2+) despatxa exactament aquest cas: tanca les que no tenen una petició en curs i respecta les que sí que en tenen.
Sobre els senyals: SIGINT és el teu Ctrl+C; SIGTERM és el que envien Docker, systemd i les plataformes de desplegament per demanar un tancament ordenat, amb un termini després del qual envien SIGKILL, que no es pot capturar. Per això el temporitzador de seguretat: més val tancar tu en 10 segons que deixar que et matin als 30 deixant fitxers a mitges. Al Mòdul 11 hi tornarem amb PM2 i Docker.
Errors Comuns i Consells
- Oblidar
res.end(). El client es queda esperant indefinidament. Al navegador es veu com una pestanya que gira sense fi, i acurlcom una resposta que no arriba mai. Tot camí d'execució del gestor ha d'acabar en unend(). - Fer E/S síncrona dins del gestor. Un
readFileSyncen una ruta bloqueja el bucle d'esdeveniments i, per tant, tots els clients alhora, no només el que la va demanar. És acceptable a l'arrencada; mai dins derequest. - Gestor
asyncsense.catch. Promesa rebutjada sense amo, client penjat i procés caigut. Embolcalla'l sempre, com facrearServidor(). - No escoltar l'esdeveniment
errordel servidor. UnEADDRINUSEet tomba el procés amb una traça incomprensible en comptes d'un missatge útil. - Confondre
connectionambrequest. Ambkeep-alive, una connexió serveix moltes peticions. Comptar connexions no és comptar visites. - Consell: exporta
crearServidor()sense arrencar-lo i arrenca només sotarequire.main === module. El teu jo del Mòdul 9, escrivint proves d'integració, t'ho agrairà. - Consell: durant el desenvolupament,
node --watch src/servidor/servidor.jsreinicia el procés en desar. Sense dependències i sensenodemon.
Exercicis
Exercici 1: comptador de connexions i peticions
Amplia src/servidor/servidor.js perquè porti el compte de connexions TCP i de peticions HTTP servides, i les exposi al resum JSON sota les claus connexions i peticions. Després, amb el servidor arrencat, executa curl tres vegades seguides i tot seguit recarrega la pàgina del navegador tres vegades. Explica per què els dos números no creixen igual.
Exercici 2: arrencada robusta
Escriu src/servidor/arrencar.js que rebi el port per process.argv (amb process.env.PORT com a segona opció i 3000 com a tercera), arrenqui el servidor i tracti els tres casos: arrencada correcta (imprimeix la URL real obtinguda de servidor.address()), EADDRINUSE (missatge clar i exitCode 1) i EACCES. Prova'l amb els ports 3000, 80 i amb dues instàncies alhora.
Exercici 3: mesurar l'aturada ordenada
Afegeix una ruta lenta al gestor: si req.url és /lent, espera 5 segons amb dormir (de src/utils/dormir.js) abans de respondre. Llança curl http://localhost:3000/lent i, mentre espera, prem Ctrl+C al servidor. Comprova que la petició es completa i que només després apareix tancat netament. Repeteix-ho traient closeIdleConnections() i explica què canvia.
Solucions
Solució 1. Els comptadors són estat del servidor, així que viuen a crearServidor:
function crearServidor() {
const estadistiques = { connexions: 0, peticions: 0 };
const servidor = http.createServer((req, res) => {
estadistiques.peticions++;
gestionarPeticio(req, res, estadistiques).catch(/* ... */);
});
servidor.on('connection', () => { estadistiques.connexions++; });
return servidor;
}Tres curl produeixen 3 connexions i 3 peticions: curl tanca la seva connexió en acabar cada invocació. Tres recàrregues del navegador produeixen normalment 1 connexió i 6 peticions o més: el navegador reutilitza la connexió gràcies a keep-alive i, a més, demana /favicon.ico pel seu compte. Aquesta asimetria és justament la raó de ser de closeIdleConnections().
Solució 2. La clau és que l'oient d'error es registri abans de listen, perquè la fallada s'emet de manera asíncrona però immediata:
const port = Number(process.argv[2]) || Number(process.env.PORT) || 3000;
const servidor = crearServidor();
servidor.on('error', (error) => {
const missatges = {
EADDRINUSE: `El port ${port} esta ocupat. Prova: node src/servidor/arrencar.js 3001`,
EACCES: `Sense permisos per al port ${port}. Fes-ne servir un >= 1024.`
};
console.error(`[servidor] ${missatges[error.code] ?? error.message}`);
process.exitCode = 1;
});
servidor.listen(port, '127.0.0.1', () => {
const { address, port } = servidor.address();
console.error(`[servidor] escoltant a http://${address}:${port}`);
});Amb el port 80 sense privilegis obtens EACCES; amb dues instàncies al 3000, la segona dóna EADDRINUSE. Sense l'oient, tots dos casos serien un abocament de pila amb Unhandled 'error' event.
Solució 3. Amb closeIdleConnections() veuràs que la petició lenta es completa i que el procés acaba un instant després. Sense ella, si abans has fet servir el navegador, el procés es queda penjat fins que expira el keep-alive (5 segons per defecte, server.keepAliveTimeout), o fins i tot fins al termini forçat de 10 segons si el navegador renova la connexió. És el símptoma exacte de "el meu servidor no tanca amb Ctrl+C", i ara ja saps que la culpa no és del teu codi, sinó d'una connexió ociosa que continua comptant.
Conclusió
Escena Viva ja és a la xarxa. I ho ha aconseguit sense conceptes nous: http.createServer torna un EventEmitter que emet request —tant se val si passes el gestor al constructor com si el registres amb on—, a més de connection, listening, close i aquest error que, si no l'escoltes, et tomba el procés igual que a la lliçó 02-05. listen(port, host, callback) és asíncron i el seu callback no és res més que un once('listening'); server.address() et dóna el port real, imprescindible quan demanes el port 0.
Tens el primer servidor real a src/servidor/servidor.js, amb la disciplina que mantindrem tot el mòdul: gestor asíncron des del minut u, .catch obligatori perquè una promesa rebutjada no et costi el procés, port configurable amb process.env.PORT i crearServidor() exportat sense arrencar. Saps provar-lo amb el navegador, amb curl -i —que ensenya les capçaleres— i amb node -e. Entens per què el procés ja no acaba tot sol: el sòcol a l'escolta manté el compte de referències del bucle d'esdeveniments, i unref() ho demostra apagant-lo. I saps arrencar-lo i aturar-lo bé: EADDRINUSE i EACCES capturats amb missatges útils, i un tancament ordenat amb SIGINT/SIGTERM, server.close() i closeIdleConnections(), perquè les connexions persistents no es tanquen soles.
El que fa el nostre servidor, però, és vergonyós: respon el mateix a /, a /esdeveniments i a /qualsevol-cosa, sempre amb un 200, sense mirar el mètode ni els paràmetres. És hora de llegir de veritat el que ens arriba i de construir amb cura el que tornem. A la lliçó següent, Gestió de Sol·licituds i Respostes, desmuntem req —que és un stream de lectura— i res —que és un stream d'escriptura—, aprenem a analitzar la URL sense tallar cadenes a mà, repassem els codis d'estat que farà servir el curs sencer i construïm src/servidor/respostes.js amb la taula que tradueix els nostres error.codi de domini a estats HTTP.
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
