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
