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