Este tutorial te guiará a través de la implementación de un CRUD en Express, utilizando una estructura de carpetas moderna y siguiendo las últimas características de ECMAScript (ES). La aplicación almacenará los datos en un archivo db.json
dentro de una carpeta database
y aplicará el patrón de arquitectura MVC (Modelo-Vista-Controlador).
Estructura del Proyecto
Primero, organizamos nuestro proyecto siguiendo esta estructura:
/project-root
│
├── /database # Carpeta para almacenamiento de datos
│ └── db.json # Archivo JSON que almacena los datos
│
├── /src
│ ├── /controllers # Controladores que manejan la lógica de negocio y peticiones HTTP
│ ├── /models # Modelos que representan las entidades de la aplicación
│ ├── /routes # Rutas que definen los endpoints de la API
│ └── index.js # Punto de entrada de la aplicación
│
├── package.json
└── README.md
Paso 1: Crear db.json
con Datos de Prueba
Primero, crea el archivo db.json
dentro de la carpeta database
con algunos datos de prueba. Este archivo será usado para almacenar los métodos de pago.
/database/db.json
{
"paymentMethods": [
{
"id": 1,
"type": "Credit Card",
"provider": "Visa",
"accountNumber": "**** **** **** 1234",
"expiry": "12/24"
},
{
"id": 2,
"type": "PayPal",
"provider": "PayPal",
"accountNumber": "user@example.com",
"expiry": ""
}
]
}
Paso 2: Modificar el Modelo
El modelo se encargará de leer y escribir datos en db.json
, utilizando fs/promises
para manejar las operaciones de archivos de forma asíncrona.
/src/models/paymentMethodModel.js
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class PaymentMethod {
constructor() {
this.filePath = path.join(__dirname, '../../database/db.json');
}
async _readData() {
const data = await fs.readFile(this.filePath, 'utf-8');
return JSON.parse(data);
}
async _writeData(data) {
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2));
}
async findAll() {
const data = await this._readData();
return data.paymentMethods;
}
async findById(id) {
const data = await this._readData();
return data.paymentMethods.find(pm => pm.id === parseInt(id));
}
async create(paymentMethod) {
const data = await this._readData();
const newMethod = {
id: data.paymentMethods.length ? data.paymentMethods.length + 1 : 1,
...paymentMethod
};
data.paymentMethods.push(newMethod);
await this._writeData(data);
return newMethod;
}
async update(id, updatedData) {
const data = await this._readData();
const index = data.paymentMethods.findIndex(pm => pm.id === parseInt(id));
if (index !== -1) {
data.paymentMethods[index] = { id: parseInt(id), ...updatedData };
await this._writeData(data);
return data.paymentMethods[index];
}
return null;
}
async delete(id) {
const data = await this._readData();
const index = data.paymentMethods.findIndex(pm => pm.id === parseInt(id));
if (index !== -1) {
data.paymentMethods.splice(index, 1);
await this._writeData(data);
return true;
}
return false;
}
}
export default new PaymentMethod();
Paso 3: Modificar el Controlador
El controlador manejará las solicitudes HTTP y delegará las operaciones de negocio al modelo.
/src/controllers/paymentMethodController.js
import PaymentMethod from '../models/paymentMethodModel.js';
class PaymentMethodController {
async getAll(req, res) {
const paymentMethods = await PaymentMethod.findAll();
res.json(paymentMethods);
}
async getById(req, res) {
const method = await PaymentMethod.findById(req.params.id);
if (method) {
res.json(method);
} else {
res.status(404).json({ message: 'Método de pago no encontrado' });
}
}
async create(req, res) {
const newMethod = await PaymentMethod.create(req.body);
res.status(201).json(newMethod);
}
async update(req, res) {
const updatedMethod = await PaymentMethod.update(req.params.id, req.body);
if (updatedMethod) {
res.json(updatedMethod);
} else {
res.status(404).json({ message: 'Método de pago no encontrado' });
}
}
async delete(req, res) {
const success = await PaymentMethod.delete(req.params.id);
if (success) {
res.status(204).send();
} else {
res.status(404).json({ message: 'Método de pago no encontrado' });
}
}
}
export default new PaymentMethodController();
Paso 4: Configurar las Rutas
Las rutas definen los endpoints de la API y asignan las rutas a los controladores correspondientes.
/src/routes/paymentMethodRoutes.js
import { Router } from 'express';
import paymentMethodController from '../controllers/paymentMethodController.js';
const router = Router();
router.get('/', paymentMethodController.getAll);
router.get('/:id', paymentMethodController.getById);
router.post('/', paymentMethodController.create);
router.put('/:id', paymentMethodController.update);
router.delete('/:id', paymentMethodController.delete);
export default router;
Paso 5: Configurar el Punto de Entrada index.js
Finalmente, el archivo index.js
actuará como el punto de entrada de la aplicación y configurará las rutas.
/src/index.js
import express from 'express';
import paymentMethodRoutes from './routes/paymentMethodRoutes.js';
const app = express();
const port = 3002;
app.use(express.json()); // Para manejar JSON en las peticiones
// Usar las rutas de paymentMethods
app.use('/payment-methods', paymentMethodRoutes);
app.listen(port, () => {
console.log(`Servidor escuchando en http://localhost:${port}`);
});
Paso 6: Configurar package.json
Asegúrate de que tu package.json
esté configurado para usar módulos ES:
package.json
{
"type": "module",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
Consideraciones finales
Con estos pasos, has creado una API REST con Express bajo el patron MVC usando EcmaScript (ES2024) para métodos de pago, utilizando un archivo JSON para el almacenamiento de datos.
Puedes probar el CRUD usando herramientas como Postman o Bruno.
Curso de Backend con Node.js: API REST con Express.js