A les dues lliçons anteriors hem escrit rutes així: 'dades/esdeveniments.json', `${DIRECTORI_BASE}/${mes}`, ruta.slice(ruta.lastIndexOf('/') + 1). Tot això funciona a la teva màquina, avui, executant des de l'arrel del projecte. I tot això està malament.
Falla si algú executa el programa des d'una altra carpeta. Falla a Windows. Falla si un nom porta un espai al lloc equivocat. I, en el cas més greu, permet que un usuari malintencionat s'escapi del directori que et pensaves que servies i s'endugui fitxers del sistema.
El mòdul path existeix per eliminar tota aquesta mena d'errors. No calcula res del disc —path és pur tractament de text i no toca el sistema de fitxers—, però coneix les regles de cada plataforma. En acabar aquesta lliçó tindràs les rutes d'Escena Viva centralitzades a src/config/rutes.js, refactoritzades les dues lliçons anteriors, i sabràs blindar una ruta construïda amb dades que vénen de fora.
Contingut
- Per què concatenar rutes és un error
joindavant deresolve: la diferència exacta- Descompondre rutes:
basename,dirname,extname,parse,format process.cwd()davant de__dirname: l'error clàssicsep,posixiwin32normalizeirelative- Seguretat: recorregut de directoris
src/config/rutes.jsi la refactorització d'Escena Viva
- Per què concatenar rutes és un error
Enganxar cadenes sembla inofensiu fins que deixes de controlar els trossos:
const directori = 'informes/';
const nom = '2026-08';
directori + '/' + nom; // 'informes//2026-08' <- barra duplicadaEls problemes concrets són quatre i tots apareixen tard:
| Problema | Exemple | Conseqüència |
|---|---|---|
| Separadors duplicats o absents | 'informes//2026-08', 'informesdades' |
Funciona de vegades; en comparacions de text, mai |
| Separador equivocat | 'informes\\2026-08' a Linux |
El nom de fitxer conté una barra invertida literal |
.. sense resoldre |
'informes/2026-08/../2026-07' |
Dues rutes diferents apunten al mateix lloc i no es detecta |
| Relativa contra absoluta | '/etc/passwd' enganxat després d'un prefix |
La barra inicial reinicia la ruta i anul·la el teu directori base |
Aquest últim és la porta del forat de seguretat de l'apartat 7. La regla és senzilla i no admet excepcions: per construir una ruta, path.join o path.resolve; mai l'operador + ni una plantilla de text.
const path = require('node:path');
path.join('informes/', '/2026-08', 'ocupacio.json');
// 'informes/2026-08/ocupacio.json' <- barres normalitzades
join davant de resolve: la diferència exacta
join davant de resolve: la diferència exactaTotes dues combinen trossos de ruta, però responen a preguntes diferents:
path.join(...)enganxa els segments i normalitza el resultat. Si el resultat és relatiu, es queda relatiu.path.resolve(...)construeix sempre una ruta absoluta, processant els arguments de dreta a esquerra i aturant-se tan bon punt ha format una ruta absoluta. Si se li acaben els arguments sense aconseguir-ho, hi anteposaprocess.cwd().
Amb el procés executant-se a /home/ana/escena-viva:
| Crida | path.join |
path.resolve |
|---|---|---|
('dades', 'esdeveniments.json') |
dades/esdeveniments.json |
/home/ana/escena-viva/dades/esdeveniments.json |
('/dades', 'esdeveniments.json') |
/dades/esdeveniments.json |
/dades/esdeveniments.json |
('dades', '/esdeveniments.json') |
dades/esdeveniments.json |
/esdeveniments.json |
('informes', '..', 'dades') |
dades |
/home/ana/escena-viva/dades |
('informes', '../..', 'x') |
../x |
/home/ana/x |
() (sense arguments) |
. |
/home/ana/escena-viva |
Les dues files destacades són les importants. A la tercera, resolve descarta tot el que hi havia a l'esquerra tan bon punt troba un argument absolut: path.resolve('dades', '/esdeveniments.json') és /esdeveniments.json, no dades/esdeveniments.json. join, en canvi, tracta la barra inicial com un simple separador i l'absorbeix.
I a la cinquena fila, join conserva el .. que sobresurt del punt de partida (no pot saber on és), mentre que resolve l'aplica sobre una ruta real i desapareix.
| Fes-lo servir quan... | Funció |
|---|---|
| Componguis segments que ja saps relatius entre si (nom de fitxer dins d'una carpeta) | join |
| Necessitis una ruta absoluta i definitiva (obrir un fitxer, comparar rutes, validar) | resolve |
| Vagis a comprovar que una ruta cau dins d'un directori permès | resolve, sempre |
- Descompondre rutes:
basename, dirname, extname, parse, format
basename, dirname, extname, parse, formatconst ruta = '/home/ana/escena-viva/informes/2026-08/ocupacio-2026-08-11.json';
path.basename(ruta); // 'ocupacio-2026-08-11.json'
path.basename(ruta, '.json'); // 'ocupacio-2026-08-11' <- sense extensio
path.dirname(ruta); // '/home/ana/escena-viva/informes/2026-08'
path.extname(ruta); // '.json' (amb el punt)path.parse torna totes les peces alhora, i path.format fa el viatge de tornada:
const parts = path.parse(ruta);
// {
// root: '/',
// dir: '/home/ana/escena-viva/informes/2026-08',
// base: 'ocupacio-2026-08-11.json',
// ext: '.json',
// name: 'ocupacio-2026-08-11'
// }
// Canviar l'extensio sense tocar text a ma:
path.format({ ...parts, base: undefined, ext: '.csv' });
// '/home/ana/escena-viva/informes/2026-08/ocupacio-2026-08-11.csv'Aquest base: undefined no és cap caprici: format dona prioritat a base per damunt de name + ext, de manera que si deixes el base original el teu canvi d'extensió s'ignora en silenci. És una d'aquelles trampes que costa mitja hora de localitzar.
Dos avisos sobre extname: en un fitxer sense punt torna la cadena buida, i a fitxer.tar.gz torna només '.gz', perquè no entén d'extensions compostes.
process.cwd() davant de __dirname: l'error clàssic
process.cwd() davant de __dirname: l'error clàssicAquest apartat explica la fallada més freqüent i més desconcertant del treball amb fitxers a Node.
process.cwd() |
__dirname |
|
|---|---|---|
| Què és | Directori des del qual es va llançar el procés | Directori del fitxer que conté aquesta variable |
| Canvia si... | L'usuari executa des d'una altra carpeta | Mai (llevat que moguis el fitxer) |
Contra què resol una ruta relativa de fs |
Contra això | Contra res: fs no el fa servir |
I aquí hi ha la clau que ho explica tot: totes les rutes relatives que passes a fs es resolen contra process.cwd(), no contra el fitxer on escrius la línia. El nostre src/cataleg-dades.js diu 'dades/esdeveniments.json', i això significa «dades/esdeveniments.json a partir d'on l'usuari hagi llançat el procés».
# Des de l'arrel del projecte: funciona.
cd ~/escena-viva
node src/cataleg.js
# ...cataleg complet...
# Des de qualsevol altre lloc: es trenca.
cd ~
node escena-viva/src/cataleg.js
# [cataleg] No es troba dades/esdeveniments.jsonEl programa és el mateix, el fitxer és al seu lloc, i falla. Perquè en el segon cas process.cwd() és /home/ana i Node busca /home/ana/dades/esdeveniments.json.
La conseqüència és més greu del que sembla: el projecte funcionarà a la teva terminal i fallarà a l'arrencada automàtica del servidor, al cron nocturn, al contenidor Docker del Mòdul 11 i a les proves del Mòdul 9, perquè en tots aquests entorns el directori de treball el decideix un altre.
La solució és ancorar les rutes al codi, no al directori de treball:
// MALAMENT: depen d'on s'executi el proces.
const ruta = 'dades/esdeveniments.json';
// BE: relativa al fitxer, sempre la mateixa.
// __dirname es src/, aixi que pugem un nivell fins a l'arrel del projecte.
const ruta = path.join(__dirname, '..', 'dades', 'esdeveniments.json');Quan sí que vols process.cwd(): quan la ruta l'escriu l'usuari a la línia d'ordres (node eina.js informes/agost.json), perquè allà el que és relatiu s'ha d'interpretar des d'on és l'usuari, no des d'on viu el teu codi. Aquesta és la regla completa: dades del projecte contra __dirname; arguments de l'usuari contra process.cwd().
sep, posix i win32
sep, posix i win32path.sep és el separador de la plataforma: '/' a Linux i macOS, '\\' a Windows. És útil per partir una ruta en segments (ruta.split(path.sep)), però no el necessites per construir: d'això ja se n'encarrega join.
El mòdul exposa a més dues variants completes:
| Variant | Separador | Quan forçar-la |
|---|---|---|
path.posix |
/ |
URL, rutes dins d'un ZIP o un contenidor, claus d'emmagatzematge al núvol, rutes guardades a base de dades |
path.win32 |
\ |
Manipular rutes de Windows des d'una altra plataforma (scripts de desplegament) |
path (per defecte) |
El de la plataforma | Tot el que sigui accés real al disc |
La distinció importa més del que sembla. Windows accepta / com a separador en obrir fitxers, així que fs funciona igual; el problema apareix quan aquesta ruta deixa de ser una ruta i es converteix en un identificador: l'URL d'un cartell d'esdeveniment, la clau d'un objecte en un emmagatzematge remot, el nom d'una entrada dins d'un ZIP. Allà informes\2026-08\cartell.png no és el mateix que informes/2026-08/cartell.png, i ningú no t'avisa.
// Ruta real al disc: la de la plataforma.
const rutaFisica = path.join(DIRECTORI_INFORMES, mes, fitxer);
// Identificador que viatjara en una URL: sempre POSIX.
const clauPublica = path.posix.join('informes', mes, fitxer);Al Mòdul 4, en servir fitxers estàtics, aquesta distinció serà obligatòria: les URL fan servir / a totes les plataformes, sense excepció.
normalize i relative
normalize i relativenormalize neteja una ruta: col·lapsa separadors repetits, resol . i .. textualment i corregeix el separador. El que no fa és tocar el disc ni resoldre enllaços simbòlics —per a això hi ha fs.realpath—, i per això un .. textual pot portar-te a un lloc diferent del real si hi ha enllaços pel mig. join i resolve ja normalitzen per dins; normalize és per quan reps una ruta ja muntada per un altre.
relative respon a «com arribo d'aquí a allà?»:
path.relative('/home/ana/escena-viva/src', '/home/ana/escena-viva/dades/esdeveniments.json');
// '../dades/esdeveniments.json'
path.relative('/home/ana/escena-viva', '/etc/passwd');
// '../../../etc/passwd' <- comenca per '..': la destinacio es FORAAquest segon exemple és la base de la comprovació de seguretat que ve ara: si la ruta relativa d'una base a una destinació comença per .., la destinació és fora de la base.
- Seguretat: recorregut de directoris
El recorregut de directoris (path traversal) és una de les vulnerabilitats més antigues i més vives del web. Apareix sempre que es construeix una ruta amb dades que vénen de fora.
Imagina el descarregador d'informes d'Escena Viva: l'usuari demana un fitxer pel seu nom i el servidor l'hi torna.
// VULNERABLE. No facis aixo.
async function descarregarInforme(nomDemanat) {
const ruta = path.join('informes', nomDemanat);
return fs.readFile(ruta, 'utf8');
}Amb nomDemanat = '2026-08/ocupacio-2026-08-11.json' tot va bé. Amb això, no:
await descarregarInforme('../../../../etc/passwd');
// path.join('informes', '../../../../etc/passwd') -> '../../../etc/passwd'
// El proces llegeix /etc/passwd i el torna per la xarxa.join normalitza, però no protegeix: aplica els .. amb tota la correcció del món i et treu del directori. I encara hi ha més variants: un nomDemanat absolut (/etc/passwd) s'escapa encara més fàcilment amb resolve, i els atacants proven a més codificacions (%2e%2e%2f), barres invertides i bytes nuls.
La defensa correcta té tres passos i no depèn de llistes negres:
// src/utils/ruta-segura.js
// Resol una ruta demanada des de fora dins d'un directori base,
// garantint que no se n'escapa.
const path = require('node:path');
function resoldreDinsDe(directoriBase, rutaDemanada) {
// 1. Base absoluta i definitiva.
const base = path.resolve(directoriBase);
// 2. Resoldre la destinacio CONTRA la base. resolve aplica els '..'.
const desti = path.resolve(base, rutaDemanada);
// 3. Comprovar que la destinacio continua a dins. Amb path.relative:
// si comenca per '..' o es absoluta, se n'ha sortit.
const desDeLaBase = path.relative(base, desti);
const dintre = desDeLaBase !== '' &&
!desDeLaBase.startsWith('..') &&
!path.isAbsolute(desDeLaBase);
if (!dintre) {
const error = new Error("Ruta fora del directori permes");
error.codi = 'RUTA_NO_PERMESA';
throw error;
}
return desti;
}
module.exports = { resoldreDinsDe };Per què aquesta comprovació és la bona:
- Es valida la ruta ja resolta, no la cadena d'entrada. Tant li fa com estigui escrita —
..,., barres repetides, barreja de separadors—: quanresolveacaba, només queda una ruta canònica, i aquesta és la que es jutja. - Es fa servir
path.relativeen comptes dedesti.startsWith(base). La comparació de prefixos té una fallada subtil:/dades/informes-privatscomença per/dades/informesi passaria el filtre sent un altre directori. Comparar prefixos exigeix recordar-se d'afegir el separador;relativeno s'equivoca. - Rebutja també la cadena buida, que significa «el directori base mateix» i gairebé mai no és un fitxer vàlid per servir.
Queda un cas que path no pot resoldre tot sol: si dins d'informes/ hi ha un enllaç simbòlic que apunta fora, la ruta resolta sembla legítima. Per blindar-ho del tot cal comprovar la destinació real amb fs.realpath i tornar a validar, o directament no permetre enllaços als directoris que es serveixen. Reprendrem tota aquesta defensa a la lliçó 04-04, on el nom del fitxer el posarà literalment l'URL d'una petició HTTP.
src/config/rutes.js i la refactorització d'Escena Viva
src/config/rutes.js i la refactorització d'Escena VivaAmb tot l'anterior, ja podem arreglar el deute de les dues lliçons prèvies. La idea és tenir un únic fitxer que sàpiga on és cada cosa, i que tot el projecte el consulti:
// src/config/rutes.js
// Unica font de veritat sobre la ubicacio dels fitxers del projecte.
// Totes les rutes son ABSOLUTES i s'ancoren al codi, no al directori
// des del qual s'executi el proces.
const path = require('node:path');
// __dirname es <arrel>/src/config, aixi que pugem dos nivells.
const ARREL = path.resolve(__dirname, '..', '..');
const DIRECTORI_DADES = path.join(ARREL, 'dades');
const DIRECTORI_INFORMES = path.join(ARREL, 'informes');
const FITXER_ESDEVENIMENTS = path.join(DIRECTORI_DADES, 'esdeveniments.json');
const FITXER_VENDES = path.join(DIRECTORI_DADES, 'vendes.csv');
module.exports = {
ARREL,
DIRECTORI_DADES,
DIRECTORI_INFORMES,
FITXER_ESDEVENIMENTS,
FITXER_VENDES
};Els canvis a la resta del projecte són petits i d'una sola línia cadascun:
// src/cataleg-dades.js
const { FITXER_ESDEVENIMENTS } = require('./config/rutes.js');
// Abans: const RUTA_ESDEVENIMENTS = 'dades/esdeveniments.json';
// Ara la constant ve de la configuracio i es absoluta.
const contingut = await fs.readFile(FITXER_ESDEVENIMENTS, 'utf8');// src/informes/magatzem-informes.js
const path = require('node:path');
const { DIRECTORI_INFORMES } = require('../config/rutes.js');
async function guardarInforme(contingut, { data = new Date(), sobreescriure = false } = {}) {
const { mes, dia } = particionarData(data);
const directori = path.join(DIRECTORI_INFORMES, mes);
const ruta = path.join(directori, `ocupacio-${dia}.json`);
// ...la resta queda igual
}I a la rotació de registres de l'exercici 3 de la lliçó anterior, el trossejat manual desapareix:
// Abans:
// const directori = ruta.slice(0, ruta.lastIndexOf('/')) || '.';
// const base = ruta.slice(ruta.lastIndexOf('/') + 1);
const directori = path.dirname(ruta);
const base = path.basename(ruta);Quatre avantatges concrets de centralitzar:
- El projecte funciona des de qualsevol directori, perquè tot penja d'
ARREL, que es calcula a partir de__dirname. - Canviar la ubicació d'una carpeta és editar una línia, no buscar vint-i-tres cadenes repetides pel codi.
- Les proves del Mòdul 9 poden substituir el mòdul sencer per un que apunti a un directori temporal.
- Es veu d'un cop d'ull quins fitxers toca l'aplicació, que és informació valuosa per revisar seguretat i desplegament.
Comprovació que el deute està saldat:
cd /tmp && node ~/escena-viva/src/cataleg.js --taula
# La taula surt igual que des de l'arrel del projecte.Errors Comuns i Consells
- Construir rutes amb
+o plantilles de text. Semprepath.joinopath.resolve. - Creure que
pathtoca el disc. No comprova existència ni resol enllaços: és manipulació de text. Per a la resta,fs.statifs.realpath. - Fer servir
path.resolveamb un segment que podria ser absolut. Descarta tot l'anterior. Si el segment ve de fora, valida abans ambresoldreDinsDe. - Validar amb
desti.startsWith(base). Deixa passar directoris germans amb prefix comú. Fes servirpath.relative. - Confiar en
process.cwd()per a les dades del projecte. Funciona a la teva terminal i es trenca alcron, a Docker i a les proves. - Fer servir
path.sepa les URL. Les URL són POSIX a totes les plataformes:path.posix.join. - Consell: guarda sempre rutes absolutes a variables i objectes interns. Converteix-les a relativa només en mostrar-les a l'usuari, amb
path.relative(ARREL, ruta). - Consell: als mòduls ES no existeix
__dirname; s'obté ambpath.dirname(fileURLToPath(import.meta.url)), com vas veure a la lliçó 02-07.
Exercicis
Exercici 1: inspector de rutes
Escriu src/laboratori/inspeccionar-ruta.js que rebi una ruta per process.argv i mostri per stdout una taula amb: la ruta tal com és, si és absoluta, la seva resolució amb resolve des de process.cwd(), el seu dirname, basename, extname, el resultat de parse, i la seva ruta relativa des d'ARREL. Prova'l amb dades/esdeveniments.json, /etc/hosts i ../../x/y.txt, executant-lo des de dos directoris diferents i explicant-ne les diferències.
Exercici 2: bateria de proves de resoldreDinsDe
Escriu src/laboratori/provar-ruta-segura.js que executi resoldreDinsDe(DIRECTORI_INFORMES, entrada) sobre aquesta llista i mostri per a cada cas si es va permetre o es va rebutjar: '2026-08/ocupacio.json', '../dades/esdeveniments.json', '/etc/passwd', '2026-08/../2026-07/x.json', '..', '', './2026-08/./x.json' i '2026-08/../../../../etc/passwd'. Raona cada resultat abans d'executar-lo.
Exercici 3: migrar el magatzem d'informes
Refactoritza src/informes/magatzem-informes.js del tot perquè no quedi ni una sola ruta construïda amb text: guardarInforme, llistarInformes i purgarInformes han de fer servir path.join sobre DIRECTORI_INFORMES, i llistarInformes ha de tornar a més un camp rutaRelativa calculat amb path.relative(ARREL, ruta), més llegible per mostrar per consola.
Solucions
Solució 1. El que és interessant no és el codi sinó l'experiment. Executat des de l'arrel del projecte i des de /tmp, la ruta dades/esdeveniments.json dona dues resolucions diferents:
cd ~/escena-viva && node src/laboratori/inspeccionar-ruta.js dades/esdeveniments.json
# resolta: /home/ana/escena-viva/dades/esdeveniments.json
cd /tmp && node ~/escena-viva/src/laboratori/inspeccionar-ruta.js dades/esdeveniments.json
# resolta: /tmp/dades/esdeveniments.json/etc/hosts dona el mateix en tots dos casos perquè ja és absoluta, i ../../x/y.txt canvia igual que la primera. Aquesta és exactament la diferència entre process.cwd() i __dirname en una sola pantalla. Fixa't que path.parse('/etc/hosts') torna ext: '' i name: 'hosts': no hi ha extensió per extreure.
Solució 2. Els resultats, amb DIRECTORI_INFORMES com a base:
| Entrada | Resultat | Per què |
|---|---|---|
2026-08/ocupacio.json |
Permesa | Cau a dins; relative dona 2026-08/ocupacio.json |
../dades/esdeveniments.json |
Rebutjada | relative dona ../dades/esdeveniments.json, comença per .. |
/etc/passwd |
Rebutjada | resolve la pren com a absoluta i descarta la base |
2026-08/../2026-07/x.json |
Permesa | Els .. es cancel·len dins de la base |
.. |
Rebutjada | Apunta al pare de la base |
'' (buida) |
Rebutjada | relative dona '': és la base mateixa, no un fitxer |
./2026-08/./x.json |
Permesa | Els . desapareixen en normalitzar |
2026-08/../../../../etc/passwd |
Rebutjada | Quatre nivells amunt surten de la base |
La conclusió pràctica: no cal enumerar els atacs. La validació no busca .. ni caràcters estranys a l'entrada; resol primer i pregunta després on ha anat a parar. Qualsevol codificació exòtica que l'atacant s'inventi acaba, després de resolve, en una ruta canònica que es jutja igual que les altres.
Solució 3. El patró es repeteix a les tres funcions: substituir cada plantilla per path.join i afegir la ruta relativa a la sortida.
const path = require('node:path');
const { ARREL, DIRECTORI_INFORMES } = require('../config/rutes.js');
// A llistarInformes, dins del bucle de fitxers:
const ruta = path.join(directori, fitxer.name);
const info = await fs.stat(ruta);
informes.push({
mes,
fitxer: fitxer.name,
ruta, // absoluta: per treballar
rutaRelativa: path.relative(ARREL, ruta), // relativa: per mostrar
midaBytes: info.size,
modificat: info.mtime.toISOString()
});Fixa't en el criteri que apareix aquí i que convé adoptar a tot el projecte: absoluta per operar, relativa només per presentar. Un console.table amb rutes absolutes de setanta caràcters és il·legible; una ruta relativa guardada en una variable i feta servir més tard és una bomba de rellotgeria.
Conclusió
Les rutes d'Escena Viva ja no depenen d'on executis el programa. Has vist per què concatenar cadenes de rutes és un error —separadors duplicats, separador equivocat, .. sense resoldre i, sobretot, un segment absolut que anul·la el teu directori base— i coneixes la diferència exacta entre join, que enganxa i normalitza conservant el caràcter relatiu, i resolve, que processa de dreta a esquerra fins a formar una ruta absoluta i descarta tot l'anterior tan bon punt troba un segment absolut.
Saps descompondre rutes amb basename, dirname, extname, parse i format —amb la trampa del base que guanya a name + ext—, i tens clar l'error clàssic que fa que un programa funcioni a la teva carpeta i falli des d'una altra: fs resol les rutes relatives contra process.cwd(), no contra el fitxer on les vas escriure. D'aquí la regla: dades del projecte ancorades a __dirname; arguments de l'usuari, contra process.cwd().
Distingeixes path.posix de path.win32 i saps que la frontera és si la ruta és un accés al disc o un identificador que viatjarà en una URL. I has construït la defensa contra el recorregut de directoris: resoldre contra la base i comprovar amb path.relative que la destinació no s'ha escapat, en comptes de perseguir .. a l'entrada o comparar prefixos de text.
Escena Viva té ara src/config/rutes.js amb ARREL, DIRECTORI_DADES, DIRECTORI_INFORMES, FITXER_ESDEVENIMENTS i FITXER_VENDES, i les dues lliçons anteriors estan refactoritzades per fer-lo servir. El projecte s'executa correctament des de qualsevol directori.
Aquest FITXER_VENDES que hem declarat apunta a un fitxer que encara no existeix, i no és casualitat. A Treballant amb Streams arriba el problema que readFile no pot resoldre: l'històric de vendes d'una temporada no cap al munt de V8. Veurem què és un stream i els seus quatre tipus, per què són EventEmitter dels que ja coneixes, què significa realment la contrapressió i què passa quan s'ignora, i processarem dades/vendes.csv línia a línia amb memòria constant.
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
