L'app-minim.js de la lliçó anterior cabia en deu línies i arrencava amb aplicacio.listen(3000). Funciona, però no es pot provar, no es pot aturar de forma ordenada, no es pot configurar per entorn i no té lloc on créixer. En aquesta lliçó el convertim en l'esquelet real d'Escena Viva: una aplicació que es crea en un lloc i s'arrenca en un altre, amb la configuració centralitzada i validada, els ajustos del framework triats a consciència i una estructura de carpetes que aguanti els sis mòduls que queden de curs.
Contingut
- La separació clau: crear l'aplicació i arrencar-la
src/app.js: la factoriacrearAplicacio()src/servidor.js: l'arrencada- Estructura de carpetes d'Escena Viva
- Ajustos de l'aplicació amb
app.setiapp.get - Configuració per entorn:
dotenvisrc/config/index.js - Muntar l'aplicació en un servidor
httppropi - Aturada ordenada amb
SIGTERM app.localsenfront deres.locals- Una nota sobre motors de vistes
- La separació clau: crear l'aplicació i arrencar-la
Regla que no trencarem a la resta del curs:
Un mòdul crea l'aplicació i la retorna. Un altre mòdul diferent la posa a escoltar.
És a dir: src/app.js exporta crearAplicacio(), que construeix l'objecte d'Express amb totes les seves rutes i middleware i el retorna sense cridar listen. I src/servidor.js importa aquesta funció, crea el servidor HTTP i el posa a escoltar.
Sembla un detall d'estil. No ho és:
| Raó | Què passaria si barregessis totes dues coses |
|---|---|
| Proves (M9) | supertest necessita l'aplicació sense arrencar: li passes l'app i ell aixeca un servidor efímer en un port lliure. Si app.js crida listen, cada fitxer de prova intentaria ocupar el port 3000 i les proves fallarien en paral·lel. |
| Aturada ordenada (M11) | Per tancar bé necessites la referència a l'objecte Server, no a l'app. Només la tens si l'arrencada és explícita. |
| Reutilització | Pots muntar diverses instàncies (API i mètriques en ports diferents) o imbricar-la amb app.use('/api/v1', crearAplicacio()). |
A més, crearAplicacio() és una factoria, no un objecte exportat directament: permet passar-li opcions i garanteix que cada prova obtingui una aplicació neta, sense estat compartit de l'anterior.
src/app.js: la factoria crearAplicacio()
src/app.js: la factoria crearAplicacio()Aquesta és la primera versió. Anirà creixent a 06-03 (routers), 06-04 (middleware propi), 06-05 (middleware de tercers) i 06-07 (errors).
// src/app.js
const express = require('express');
const { configuracio } = require('./config/index.js');
/**
* Construeix l'aplicacio Express d'Escena Viva.
* No arrenca cap servidor: nomes retorna l'aplicacio configurada.
*/
function crearAplicacio(opcions = {}) {
const { servirEstatics = true } = opcions;
const aplicacio = express();
// --- Ajustos del framework (apartat 5) ---
aplicacio.disable('x-powered-by');
aplicacio.set('trust proxy', configuracio.confiarEnProxy);
aplicacio.set('case sensitive routing', true);
aplicacio.set('json spaces', configuracio.esProduccio ? 0 : 2);
// --- Valors compartits per tota l'aplicacio (apartat 9) ---
aplicacio.locals.nomPlataforma = 'Escena Viva';
aplicacio.locals.versio = require('../package.json').version;
// --- Middleware integrat: lectura del cos JSON i estatics ---
aplicacio.use(express.json({ limit: configuracio.limitCos }));
if (servirEstatics) {
aplicacio.use(express.static(configuracio.directoriPublic, { index: 'index.html' }));
}
// --- Rutes (s'ompliran a 06-03) ---
aplicacio.get('/salut', (peticio, resposta) => {
const { nomPlataforma, versio } = aplicacio.locals;
resposta.json({ estat: 'ok', plataforma: nomPlataforma, versio });
});
return aplicacio;
}
module.exports = { crearAplicacio };Punts que val la pena assenyalar: no hi ha cap listen enlloc (aquest és el contracte); la funció rep opcions amb valors per defecte, de manera que crearAplicacio() sense arguments continua funcionant; la configuració arriba d'un mòdul, no de process.env escampat pel fitxer (apartat 6); i els diagnòstics no s'imprimeixen aquí, perquè app.js no parla per consola, només construeix.
src/servidor.js: l'arrencada
src/servidor.js: l'arrencada// src/servidor.js
const http = require('node:http');
const { crearAplicacio } = require('./app.js');
const { configuracio } = require('./config/index.js');
/** Crea el servidor HTTP i el posa a escoltar. */
function arrencarServidor() {
// Creem el servidor a ma en comptes de fer servir aplicacio.listen(...):
// aixi conservem la referencia al Server per tancar-lo (apartats 7 i 8).
const servidor = http.createServer(crearAplicacio());
const { port, host, entorn } = configuracio;
servidor.listen(port, host, () => {
console.error(`[escena-viva] entorn=${entorn} escoltant a http://${host}:${port}`);
});
registrarAturadaOrdenada(servidor);
return servidor;
}
module.exports = { arrencarServidor };
// El fitxer es modul i script alhora.
if (require.main === module) arrencarServidor();La funció registrarAturadaOrdenada l'escriurem a l'apartat 8. Actualitza també els scripts del package.json, perquè el punt d'entrada ha canviat respecte del mòdul 4: "start": "node src/servidor.js" i "dev": "node --watch src/servidor.js".
- Estructura de carpetes d'Escena Viva
Express no imposa estructura. Aquesta és la nostra, pensada perquè cada fitxer tingui una sola raó per canviar.
escena-viva/
├── dades/ # esdeveniments.json, vendes.csv (ja existia)
├── public/ # index.html, estils.css, app.js (ja existia)
├── src/
│ ├── app.js # crearAplicacio()
│ ├── servidor.js # arrencada i aturada
│ ├── config/ # index.js (NOU) + rutes.js (ja existia)
│ ├── rutes/ # routers d'Express (06-03)
│ ├── controladors/ # gestors (req, res) (06-03)
│ ├── middleware/ # middleware propi (06-04)
│ ├── esquemes/ # validacio amb zod (06-06)
│ ├── errors.js # jerarquia d'errors (06-07)
│ ├── domini/ # Esdeveniment, Sessio, GestorDeVendes (ja existia)
│ ├── serveis/ # canvi-divises.js i logica de negoci
│ ├── cataleg-dades.js # acces al JSON (ja existia)
│ ├── informes/ streams/ utils/ i servidor/ (l'artesanal del M4, referencia)
└── package.jsonResponsabilitats, perquè ningú no tingui dubtes d'on va cada cosa:
| Carpeta | Responsabilitat | Què no ha de contenir |
|---|---|---|
rutes/ |
Declarar quina URL crida quin controlador i quin middleware la protegeix | Lògica de negoci |
controladors/ |
Traduir HTTP a domini: llegir req, cridar un servei, respondre |
Regles d'aforament, accés a fitxers |
serveis/ |
Casos d'ús: orquestrar domini i dades | Objectes req/res |
domini/ |
Regles d'Escena Viva: aforament, estats, preus | Res d'HTTP ni d'Express |
middleware/ |
Preocupacions transversals: registre, identificador, memòria cau | Regles específiques d'un endpoint |
esquemes/ i config/ |
Forma esperada de l'entrada; lectura i validació de l'entorn | Consultes, respostes ni lògica |
La regla que ho resumeix: el domini no importa Express, i Express no importa fs. Si un fitxer de domini/ necessita require('express'), hi ha alguna cosa mal col·locada.
Advertiment contra la sobreenginyeria
Aquesta estructura té sentit per a una API que creixerà durant sis mòduls més. Per a un projecte de tres endpoints és exagerada. Senyals que t'estàs passant: controladors que només criden un servei que només crida un repositori que només fa una línia (tres fitxers per a una operació trivial), carpetes buides «per si de cas», i interfícies i abstraccions per a una única implementació que mai no en tindrà cap altra.
Comença per rutes/ + controladors/ i treu-ne serveis/ quan la lògica es repeteixi en dos controladors, no abans. A Escena Viva sí que arribarem a aquest punt, perquè la venda d'entrades es fa servir des de l'API i des dels informes.
- Ajustos de l'aplicació amb
app.set i app.get
app.set i app.getExpress desa els ajustos en un diccionari intern. app.set(nom, valor) escriu, app.get(nom) llegeix (compte: app.get amb dos arguments registra una ruta GET; amb un, llegeix un ajust), i app.enable/app.disable són dreceres per a valors booleans.
| Ajust | Valor recomanat | Què fa i per què |
|---|---|---|
env |
process.env.NODE_ENV o development |
En producció s'activa la memòria cau de vistes i el gestor d'errors deixa d'enviar la traça (06-07) |
x-powered-by |
desactivat | Express afegeix X-Powered-By: Express a cada resposta: informació gratuïta per a qui busca objectius amb una versió coneguda. No és cap gran defensa, però no costa res |
trust proxy |
1 o la llista de proxies de confiança |
Darrere d'Nginx o d'un balancejador, req.ip és la IP del proxy i req.protocol és http encara que el client vingui per HTTPS. Amb aquest ajust, Express llegeix X-Forwarded-For i X-Forwarded-Proto. Imprescindible per al limitador de peticions de 06-05 i per al registre del mòdul 11 |
json spaces |
2 en desenvolupament, 0 en producció |
Indenta el JSON de res.json(). Còmode en depurar amb curl, malbaratament d'amplada de banda en producció |
case sensitive routing |
true |
Sense això, /Esdeveniments i /esdeveniments són la mateixa ruta. Que dues URL diferents retornin el mateix perjudica la memòria cau i el SEO |
strict routing |
false |
Amb false, /esdeveniments i /esdeveniments/ són la mateixa ruta (comportament amable). Amb true, són diferents. El nostre normalitzarRuta del mòdul 4 feia justament això a mà |
query parser |
'simple' o 'extended' |
Com s'interpreten les query strings imbricades (?filtre[sala]=Bóveda). 'simple' fa servir querystring del nucli; 'extended' fa servir qs i admet imbricació |
Des d'un gestor els pots llegir amb aplicacio.get('env') o aplicacio.get('trust proxy'); d'aquest últim depenen peticio.ip i peticio.protocol.
Compte amb
trust proxy. Activa'l només si realment hi ha un proxy al davant. Exposat directament a internet, qualsevol pot falsificar la seva IP enviant una capçaleraX-Forwarded-For, i el teu limitador de peticions deixa de servir de res. Per això a la nostra configuració és un valor d'entorn, no una constant.
- Configuració per entorn:
dotenv i src/config/index.js
dotenv i src/config/index.jsdotenv ja és dependència des del mòdul 5. La seva feina és carregar un fitxer .env dins de process.env. Res més: no valida ni converteix tipus. Això ho fem nosaltres.
# .env (NO es puja al repositori; al repositori hi va .env.exemple)
NODE_ENV=desenvolupament
PORT=3000
LIMIT_COS=100kb
CONFIAR_EN_PROXY=0
ORIGENS_PERMESOS=http://localhost:5173,http://127.0.0.1:5173// src/config/index.js
const path = require('node:path');
require('dotenv').config();
const { ARREL } = require('./rutes.js');
// Lectors tipats: cadascun llanca si el valor no compleix l'esperat.
const opcional = (nom, perDefecte) => (process.env[nom] ?? '').trim() || perDefecte;
function obligatoria(nom) {
const valor = opcional(nom, '');
if (valor === '') throw new Error(`Falta la variable obligatoria: ${nom}`);
return valor;
}
function enter(nom, perDefecte, { minim, maxim }) {
const numero = Number.parseInt(opcional(nom, String(perDefecte)), 10);
if (!Number.isInteger(numero) || numero < minim || numero > maxim) {
throw new Error(`La variable ${nom} ha de ser un enter entre ${minim} i ${maxim}`);
}
return numero;
}
const llista = (n) => opcional(n, '').split(',').map((e) => e.trim()).filter(Boolean);
const ENTORNS_VALIDS = ['desenvolupament', 'proves', 'produccio'];
function construirConfiguracio() {
const entorn = opcional('NODE_ENV', 'desenvolupament');
if (!ENTORNS_VALIDS.includes(entorn)) {
throw new Error(`NODE_ENV ha de ser un de: ${ENTORNS_VALIDS.join(', ')}`);
}
return Object.freeze({
entorn,
esProduccio: entorn === 'produccio',
port: enter('PORT', 3000, { minim: 1, maxim: 65535 }),
host: opcional('HOST', '127.0.0.1'),
limitCos: opcional('LIMIT_COS', '100kb'),
confiarEnProxy: enter('CONFIAR_EN_PROXY', 0, { minim: 0, maxim: 10 }),
origensPermesos: llista('ORIGENS_PERMESOS'),
directoriPublic: path.join(ARREL, 'public'),
// En produccio exigim clau de signatura; en desenvolupament hi ha valor de farciment.
clauPassarella: entorn === 'produccio'
? obligatoria('CLAU_PASSARELLA')
: opcional('CLAU_PASSARELLA', 'clau-de-desenvolupament'),
});
}
let configuracio;
try {
configuracio = construirConfiguracio();
} catch (error) {
console.error(`[configuracio] ${error.message}`); // fallar rapid
process.exit(1);
}
module.exports = { configuracio, ENTORNS_VALIDS };Les idees que hi ha darrere d'aquest fitxer:
- Un únic punt de lectura de
process.env. Buscar-lo al projecte ha de donar exactament un resultat: aquest fitxer. Així saps d'un cop d'ull què necessita l'aplicació per arrencar. - Fallar ràpid. Si falta la clau de la passarel·la en producció, el procés mor a l'arrencada amb un missatge clar. L'alternativa —arrencar i fallar tres hores després, a la primera venda— és molt pitjor.
- Tipus correctes.
process.env.PORTés la cadena'3000'; la configuració exposa el número3000i evita comparacions sorpresa. - Objecte congelat amb
Object.freeze: ningú no canvia la configuració en temps d'execució. - El
.envno va mai al repositori. Es versiona.env.exemple; afegeix.enval.gitignore.
Al mòdul 11 ampliarem això amb secrets gestionats; de moment, amb això n'hi ha prou.
- Muntar l'aplicació en un servidor
http propi
http propiA 06-01 vam veure que app és una funció gestora. Aquí ho aprofitem: escrivim http.createServer(aplicacio) en comptes d'aplicacio.listen(...). Què hi guanyes exactament?
| Avantatge | Per a què el necessitaràs |
|---|---|
Tens la referència al Server |
Tancar-lo amb servidor.close() a l'aturada ordenada (apartat 8) |
| Pots acoblar altres protocols al mateix port | Socket.IO al mòdul 12 fa new Server(servidor) |
| Pots ajustar temps d'espera | servidor.keepAliveTimeout, servidor.headersTimeout al mòdul 11 |
| Pots escoltar esdeveniments del servidor | servidor.on('error', ...) per capturar EADDRINUSE amb un missatge decent |
| Pots tenir dos servidors amb la mateixa app | HTTP i HTTPS, o un port de mètriques a part |
// L'error classic: el port ja esta ocupat.
servidor.on('error', (error) => {
if (error.code !== 'EADDRINUSE') throw error;
console.error(`[escena-viva] el port ${configuracio.port} ja esta en us`);
process.exit(1);
});
- Aturada ordenada amb
SIGTERM
SIGTERMQuan un orquestrador (PM2, Docker, Kubernetes) vol aturar el teu procés, li envia el senyal SIGTERM. Si no fas res, Node mor immediatament i les peticions en curs es tallen a mitges: un comprador podria veure el navegador penjat just després d'enviar una comanda. L'aturada ordenada consisteix a deixar d'acceptar connexions noves, acabar les que estan en curs, tancar recursos i sortir.
// src/servidor.js (continuacio)
const MILLISEGONS_DE_GRACIA = 10_000;
function registrarAturadaOrdenada(servidor) {
let aturant = false;
function aturar(senyal) {
if (aturant) return; // un segon senyal no duplica l'aturada
aturant = true;
console.error(`[escena-viva] rebut ${senyal}, tancant ordenadament...`);
// 1. Deixem d'acceptar connexions noves; les que hi ha en curs acaben.
servidor.close((error) => {
if (error) console.error('[escena-viva] error en tancar:', error.message);
process.exit(error ? 1 : 0);
});
// 2. Tanquem les connexions keep-alive ocioses per no esperar que expirin.
servidor.closeIdleConnections();
// 3. Xarxa de seguretat; unref evita que el temporitzador retingui el proces.
setTimeout(() => {
console.error('[escena-viva] tancament forcat despres del temps de gracia');
process.exit(1);
}, MILLISEGONS_DE_GRACIA).unref();
}
process.on('SIGTERM', () => aturar('SIGTERM'));
process.on('SIGINT', () => aturar('SIGINT')); // Ctrl+C en desenvolupament
}Detalls que la gent se sol saltar: l'indicador aturant evita que prémer Ctrl+C dues vegades llanci dues aturades simultànies; sense closeIdleConnections() una connexió keep-alive ociosa manté el servidor viu fins que expiri i el tancament s'eternitza; i el temporitzador amb unref() garanteix que el procés acabi encara que alguna cosa s'encalli, sense impedir que acabi abans si tot va bé. Aquí és on al mòdul 11 hi afegirem el tancament de la base de dades i el bolcat dels registres pendents.
app.locals enfront de res.locals
app.locals enfront de res.localsDos magatzems amb noms semblants i vides molt diferents.
app.locals |
res.locals |
|
|---|---|---|
| Àmbit | Tota l'aplicació | Una única petició/resposta |
| Vida | Des de l'arrencada fins a l'aturada | Des que arriba la petició fins que es respon |
| Compartit entre usuaris | Sí | No |
| Ús típic | Nom de la plataforma, versió, constants | Identificador de petició, usuari autenticat, instant d'inici |
// Globals: es fixen una vegada, a crearAplicacio().
aplicacio.locals.sales = ['Teatro Almendra', 'Sala Boveda', 'Auditorio Ribera'];
// Per peticio: els posa un middleware (06-04).
aplicacio.use((peticio, resposta, seguent) => {
resposta.locals.idPeticio = crypto.randomUUID();
seguent();
});Error greu i freqüent: desar dades de l'usuari actual a
app.locals. Com que és global, el segon usuari veuria les dades del primer. Tot el que depengui de qui fa la petició va ares.locals(o areq, com farem ambreq.esdevenimentireq.dadesValidades).
- Una nota sobre motors de vistes
Express sap renderitzar plantilles al servidor: app.set('view engine', 'ejs'), app.set('views', ...) i res.render('esdeveniment', { esdeveniment }). És la forma clàssica de generar HTML des de Node i continua sent vàlida per a llocs amb contingut servit.
Escena Viva no la fa servir. El nostre front-end és la carpeta public/ (HTML, CSS i un app.js que crida l'API amb fetch), i el servidor és una API JSON: la mateixa API serveix un front-end web, una app mòbil o un panell intern, i les seves respostes es proven amb supertest sense analitzar HTML. El que sí que conservem és express.static, que serveix aquests fitxers i substitueix el teu estatics.js del mòdul 4; el desmuntarem a 06-04.
Errors Comuns i Consells
- Cridar
listen()dins d'app.js. Trenca les proves del mòdul 9 i l'aturada del mòdul 11. Si veus unlistenfora deservidor.js, és un error. - Exportar l'app ja construïda (
module.exports = app) en comptes d'una factoria. Funciona fins que una prova necessita una altra configuració; llavors cal refactoritzar-ho tot. process.envescampat per vint fitxers. Impossible saber què necessita l'aplicació. Centralitza-ho aconfig/index.js.- Confondre
app.get('nom')ambapp.get('/ruta', gestor). Un argument llegeix un ajust; dos registren una ruta. - Activar
trust proxysense proxy. Regales la capacitat de falsificar la IP a qualsevol. - Pujar el
.enval repositori. Al.gitignoredes del primer dia; versiona.env.exemple. - Consell: executa
node -e "require('./src/config/index.js')"a l'scriptcomprovar. Si la configuració és invàlida, ho sabràs a la integració contínua i no en producció.
Exercicis
Exercici 1: la separació a la pràctica
Crea src/app.js i src/servidor.js amb l'estructura d'aquesta lliçó i demostra que la separació funciona: escriu un tercer fitxer provar-app.js que importi crearAplicacio(), la munti en un http.createServer sobre el port 0 (Node n'assigna un de lliure), faci una petició a /salut amb fetch i tanqui el servidor. No ha de tocar src/servidor.js en cap moment.
Exercici 2: configuració que falla ràpid
Amplia src/config/index.js amb una variable MONEDA_PER_DEFECTE que només admeti EUR, USD o GBP, amb EUR per defecte. Comprova que un valor invàlid impedeix l'arrencada amb un missatge clar i codi de sortida 1.
Exercici 3: aturada observable
Afegeix a l'aturada ordenada un comptador de peticions en curs: un middleware l'incrementa en entrar i el decrementa a res.on('finish'). En rebre SIGTERM, el servidor ha d'imprimir per stderr quantes peticions quedaven vives. Prova-ho amb un endpoint lent (/lent, que triga 3 segons) i enviant el senyal mentre està en curs.
Solucions
Solució 1
// provar-app.js
const http = require('node:http');
const { crearAplicacio } = require('./src/app.js');
async function principal() {
const servidor = http.createServer(crearAplicacio({ servirEstatics: false }));
// Port 0: el sistema operatiu n'assigna un de lliure. Es el que fa supertest.
await new Promise((resoldre) => servidor.listen(0, '127.0.0.1', resoldre));
const resposta = await fetch(`http://127.0.0.1:${servidor.address().port}/salut`);
console.log(JSON.stringify({ estat: resposta.status, cos: await resposta.json() }));
await new Promise((resoldre) => servidor.close(resoldre));
}
principal().catch((error) => {
console.error('[provar-app]', error.message);
process.exitCode = 1;
});Que això sigui possible sense arrencar src/servidor.js és exactament la prova que la separació està ben feta.
Solució 2
// Dins de src/config/index.js
const MONEDES_VALIDES = ['EUR', 'USD', 'GBP'];
function moneda(nom, perDefecte) {
const valor = opcional(nom, perDefecte).toUpperCase();
if (!MONEDES_VALIDES.includes(valor)) {
throw new Error(`${nom} ha de ser una de: ${MONEDES_VALIDES.join(', ')}`);
}
return valor;
}
// ...dins de construirConfiguracio(): monedaPerDefecte: moneda('MONEDA_PER_DEFECTE', 'EUR'),MONEDA_PER_DEFECTE=IEN node src/servidor.js
# [configuracio] MONEDA_PER_DEFECTE ha de ser una de: EUR, USD, GBP (sortida: 1)Solució 3
// src/servidor.js — fragments amb el comptador
let enCurs = 0;
// Registrat el PRIMER per comptar absolutament tot.
aplicacio.use((peticio, resposta, seguent) => {
enCurs += 1;
resposta.on('finish', () => { enCurs -= 1; });
seguent();
});
aplicacio.get('/lent', (p, r) => setTimeout(() => r.json({ estat: 'ok' }), 3000));
process.on('SIGTERM', () => {
console.error(`[escena-viva] SIGTERM amb ${enCurs} peticions en curs`);
servidor.close(() => process.exit(0));
servidor.closeIdleConnections();
});Detall important: perquè el comptador compti, cal registrar-lo abans que les rutes. Aquest és el tema central de 06-04.
Conclusió
Ja no tens un script de joguina, sinó l'esquelet d'una aplicació de debò. La peça central és la separació entre crearAplicacio() —que construeix i retorna l'app sense arrencar-la— i src/servidor.js —que crea el servidor http, el posa a escoltar i sap aturar-lo ordenadament quan arriba SIGTERM—. Aquesta separació no és estètica: és el que farà possible provar amb supertest al mòdul 9, acoblar Socket.IO al mòdul 12 i desplegar amb PM2 i Docker al mòdul 11. A més has definit l'estructura de carpetes d'Escena Viva amb responsabilitats clares (i saps quan no aplicar-la), has triat a consciència els ajustos del framework (x-powered-by fora, trust proxy segons entorn, case sensitive routing actiu) i has centralitzat la configuració en un mòdul que llegeix process.env una sola vegada, valida tipus i rangs i falla ràpid si alguna cosa no quadra.
L'app, però, té una única ruta: /salut. A la lliçó següent, Enrutament a Express, l'omplim: paràmetres de ruta comparats amb el compilarPatro que vas escriure a mà, express.Router() per muntar /api/esdeveniments, /api/sessions i /api/comandes com a mòduls independents, router.param() per carregar l'esdeveniment una sola vegada, i la regla que evita més maldecaps: l'ordre de declaració de les rutes.
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
