En aquest tema, aprendrem a desenvolupar una plataforma de blogs utilitzant Node.js i Express. Aquest projecte ens permetrà aplicar molts dels conceptes apresos al llarg del curs, com ara la gestió de rutes, l'autenticació, la interacció amb bases de dades i la creació d'APIs RESTful.
Objectius del Tema
- Crear una aplicació de blogs amb Node.js i Express.
 - Implementar operacions CRUD (Crear, Llegir, Actualitzar, Eliminar) per a les entrades del blog.
 - Gestionar l'autenticació i l'autorització dels usuaris.
 - Utilitzar una base de dades per emmagatzemar les dades del blog.
 - Implementar una interfície d'usuari bàsica per interactuar amb l'API.
 
Requisits Previs
- Coneixements bàsics de Node.js i Express.
 - Familiaritat amb MongoDB i Mongoose.
 - Comprensió de les operacions CRUD.
 - Coneixements bàsics d'HTML i CSS per a la interfície d'usuari.
 
Passos per Desenvolupar la Plataforma de Blogs
- Configuració del Projecte
 
1.1. Crear l'Estructura del Projecte
mkdir blog-platform cd blog-platform npm init -y npm install express mongoose body-parser bcryptjs jsonwebtoken
1.2. Estructura de Carpetes
blog-platform/ │ ├── models/ │ ├── user.js │ └── post.js │ ├── routes/ │ ├── auth.js │ └── posts.js │ ├── controllers/ │ ├── authController.js │ └── postController.js │ ├── middleware/ │ └── authMiddleware.js │ ├── views/ │ ├── index.html │ └── post.html │ ├── app.js └── config.js
- Configuració de l'Aplicació
 
2.1. Crear app.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const config = require('./config');
const app = express();
// Middleware
app.use(bodyParser.json());
// Conectar a MongoDB
mongoose.connect(config.dbUri, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('MongoDB connected'))
    .catch(err => console.log(err));
// Rutes
const authRoutes = require('./routes/auth');
const postRoutes = require('./routes/posts');
app.use('/api/auth', authRoutes);
app.use('/api/posts', postRoutes);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));2.2. Crear config.js
module.exports = {
    dbUri: 'mongodb://localhost:27017/blog-platform',
    jwtSecret: 'your_jwt_secret'
};
- Models
 
3.1. Model d'Usuari (models/user.js)
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
    username: { type: String, required: true, unique: true },
    password: { type: String, required: true }
});
UserSchema.pre('save', async function(next) {
    if (!this.isModified('password')) return next();
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
});
UserSchema.methods.matchPassword = async function(password) {
    return await bcrypt.compare(password, this.password);
};
module.exports = mongoose.model('User', UserSchema);3.2. Model de Post (models/post.js)
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
    title: { type: String, required: true },
    content: { type: String, required: true },
    author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
    createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Post', PostSchema);
- Rutes i Controladors
 
4.1. Rutes d'Autenticació (routes/auth.js)
const express = require('express');
const { register, login } = require('../controllers/authController');
const router = express.Router();
router.post('/register', register);
router.post('/login', login);
module.exports = router;4.2. Controlador d'Autenticació (controllers/authController.js)
const User = require('../models/user');
const jwt = require('jsonwebtoken');
const config = require('../config');
exports.register = async (req, res) => {
    const { username, password } = req.body;
    try {
        const user = new User({ username, password });
        await user.save();
        res.status(201).json({ message: 'User registered successfully' });
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};
exports.login = async (req, res) => {
    const { username, password } = req.body;
    try {
        const user = await User.findOne({ username });
        if (!user || !(await user.matchPassword(password))) {
            return res.status(401).json({ message: 'Invalid credentials' });
        }
        const token = jwt.sign({ id: user._id }, config.jwtSecret, { expiresIn: '1h' });
        res.json({ token });
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};4.3. Middleware d'Autenticació (middleware/authMiddleware.js)
const jwt = require('jsonwebtoken');
const config = require('../config');
const User = require('../models/user');
exports.protect = async (req, res, next) => {
    let token;
    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
        token = req.headers.authorization.split(' ')[1];
    }
    if (!token) {
        return res.status(401).json({ message: 'Not authorized' });
    }
    try {
        const decoded = jwt.verify(token, config.jwtSecret);
        req.user = await User.findById(decoded.id);
        next();
    } catch (err) {
        res.status(401).json({ message: 'Not authorized' });
    }
};4.4. Rutes de Posts (routes/posts.js)
const express = require('express');
const { getPosts, createPost, updatePost, deletePost } = require('../controllers/postController');
const { protect } = require('../middleware/authMiddleware');
const router = express.Router();
router.route('/')
    .get(getPosts)
    .post(protect, createPost);
router.route('/:id')
    .put(protect, updatePost)
    .delete(protect, deletePost);
module.exports = router;4.5. Controlador de Posts (controllers/postController.js)
const Post = require('../models/post');
exports.getPosts = async (req, res) => {
    try {
        const posts = await Post.find().populate('author', 'username');
        res.json(posts);
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};
exports.createPost = async (req, res) => {
    const { title, content } = req.body;
    try {
        const post = new Post({ title, content, author: req.user._id });
        await post.save();
        res.status(201).json(post);
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};
exports.updatePost = async (req, res) => {
    const { id } = req.params;
    const { title, content } = req.body;
    try {
        const post = await Post.findById(id);
        if (!post) {
            return res.status(404).json({ message: 'Post not found' });
        }
        if (post.author.toString() !== req.user._id.toString()) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        post.title = title;
        post.content = content;
        await post.save();
        res.json(post);
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};
exports.deletePost = async (req, res) => {
    const { id } = req.params;
    try {
        const post = await Post.findById(id);
        if (!post) {
            return res.status(404).json({ message: 'Post not found' });
        }
        if (post.author.toString() !== req.user._id.toString()) {
            return res.status(401).json({ message: 'Not authorized' });
        }
        await post.remove();
        res.json({ message: 'Post removed' });
    } catch (err) {
        res.status(400).json({ error: err.message });
    }
};
- Interfície d'Usuari
 
5.1. Crear views/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog Platform</title>
</head>
<body>
    <h1>Welcome to the Blog Platform</h1>
    <div id="posts"></div>
    <script>
        async function fetchPosts() {
            const response = await fetch('/api/posts');
            const posts = await response.json();
            const postsDiv = document.getElementById('posts');
            postsDiv.innerHTML = posts.map(post => `
                <div>
                    <h2>${post.title}</h2>
                    <p>${post.content}</p>
                    <p>Author: ${post.author.username}</p>
                </div>
            `).join('');
        }
        fetchPosts();
    </script>
</body>
</html>
- Resum
 
En aquest tema, hem creat una plataforma de blogs utilitzant Node.js i Express. Hem configurat l'estructura del projecte, creat models per a usuaris i posts, implementat rutes i controladors per gestionar l'autenticació i les operacions CRUD, i hem creat una interfície d'usuari bàsica per mostrar les entrades del blog. Aquest projecte ens ha permès aplicar molts dels conceptes apresos al llarg del curs i ens ha proporcionat una base sòlida per desenvolupar aplicacions web amb Node.js.
Exercicis Pràctics
- 
Ampliar la Funcionalitat de l'Aplicació:
- Afegir la funcionalitat per a que els usuaris puguin comentar les entrades del blog.
 - Implementar la funcionalitat de "like" per a les entrades del blog.
 
 - 
Millorar la Interfície d'Usuari:
- Utilitzar un framework CSS com Bootstrap per millorar l'aparença de la interfície d'usuari.
 - Afegir formularis per crear i editar entrades del blog des de la interfície d'usuari.
 
 - 
Seguretat:
- Implementar la validació de dades al servidor per assegurar-se que les entrades del blog continguin informació vàlida.
 - Utilitzar HTTPS per assegurar la comunicació entre el client i el servidor.
 
 - 
Desplegament:
- Desplegar l'aplicació a una plataforma com Heroku o Vercel.
 - Configurar variables d'entorn per gestionar la configuració de l'aplicació en diferents entorns (desenvolupament, producció).
 
 
Solucions als Exercicis
Les solucions als exercicis pràctics es poden trobar a la documentació oficial de Node.js, Express, i MongoDB, així com en diversos tutorials i recursos en línia. Es recomana als estudiants que intentin resoldre els exercicis per si mateixos abans de buscar les solucions, ja que això els ajudarà a consolidar els coneixements adquirits.
Curs de Node.js
Mòdul 1: Introducció a Node.js
Mòdul 2: Conceptes Bàsics
Mòdul 3: Sistema de Fitxers i I/O
Mòdul 4: HTTP i Servidors Web
Mòdul 5: NPM i Gestió de Paquets
Mòdul 6: Framework Express.js
- Introducció a Express.js
 - Configuració d'una Aplicació Express
 - Middleware
 - Routing en Express
 - Gestió d'Errors
 
Mòdul 7: Bases de Dades i ORMs
- Introducció a les Bases de Dades
 - Utilitzar MongoDB amb Mongoose
 - Utilitzar Bases de Dades SQL amb Sequelize
 - Operacions CRUD
 
Mòdul 8: Autenticació i Autorització
Mòdul 9: Proves i Depuració
- Introducció a les Proves
 - Proves Unitàries amb Mocha i Chai
 - Proves d'Integració
 - Depuració d'Aplicacions Node.js
 
Mòdul 10: Temes Avançats
Mòdul 11: Desplegament i DevOps
- Variables d'Entorn
 - Utilitzar PM2 per a la Gestió de Processos
 - Desplegar a Heroku
 - Integració i Desplegament Continu
 
