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).
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 lalógica de negocio y peticiones HTTP
│ ├── /models # Modelos que representan las entidades dela aplicación
│ ├── /routes # Rutas que definen los endpoints dela API
│ └── index.js # Punto de entrada dela aplicación
│
├── package.json
└── README.md
db.json
con Datos de PruebaPrimero, 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": "[email protected]",
"expiry": ""
}
]
}
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);
classPaymentMethod{
constructor() {
this.filePath = path.join(__dirname, '../../database/db.json');
}
async _readData() {
const data = await fs.readFile(this.filePath, 'utf-8');
returnJSON.parse(data);
}
async _writeData(data) {
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2));
}
async findAll() {
const data = awaitthis._readData();
return data.paymentMethods;
}
async findById(id) {
const data = awaitthis._readData();
return data.paymentMethods.find(pm => pm.id === parseInt(id));
}
async create(paymentMethod) {
const data = awaitthis._readData();
const newMethod = {
id: data.paymentMethods.length ? data.paymentMethods.length + 1 : 1,
...paymentMethod
};
data.paymentMethods.push(newMethod);
awaitthis._writeData(data);
return newMethod;
}
async update(id, updatedData) {
const data = awaitthis._readData();
const index = data.paymentMethods.findIndex(pm => pm.id === parseInt(id));
if (index !== -1) {
data.paymentMethods[index] = { id: parseInt(id), ...updatedData };
awaitthis._writeData(data);
return data.paymentMethods[index];
}
returnnull;
}
asyncdelete(id) {
const data = awaitthis._readData();
const index = data.paymentMethods.findIndex(pm => pm.id === parseInt(id));
if (index !== -1) {
data.paymentMethods.splice(index, 1);
awaitthis._writeData(data);
returntrue;
}
returnfalse;
}
}
exportdefaultnew PaymentMethod();
El controlador manejará las solicitudes HTTP y delegará las operaciones de negocio al modelo.
/src/controllers/paymentMethodController.js
import PaymentMethod from'../models/paymentMethodModel.js';
classPaymentMethodController{
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' });
}
}
asyncdelete(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' });
}
}
}
exportdefaultnew PaymentMethodController();
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);
exportdefault router;
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}`);
});
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"
}
}
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.