Introducci贸n a Node.js y Express
Node.js y Express
Entorno de trabajo
Variables de entorno
Rutas din谩micas
Body parser
Postman
CRUD
驴Que es una API Restful?
Solicitudes GET
Solicitudes POST
CRUD
Soluci贸n del reto de validaci贸n
Solicitudes DELETE
MIddlewares
Middlewares - logger
Middlewares - ErrorHandler
DB
Instalar Postgresql
Instalar Prisma y PostgreSQL Client
Prisma Models
JWT
Autenticaci贸n utilizando JSON Web Tokens (JWT)
Auth - Register
Auth - Login
Expres.js
Arquitectura
Arquitectura parte 2
Creaci贸n y Migraci贸n de Modelos con Prisma para Citas M茅dicas
Admin controllers / services
Admin controllers / services parte 2
Reservations
Reservations parte 2
Appointments
Deploy
PostgreSQL
Deploy
You don't have access to this class
Keep learning! Join and start boosting your career
Booking management in web applications is an essential component for many modern businesses. Implementing a robust system that allows creating, querying, updating and deleting appointments requires an organized structure and proper handling of potential conflicts. In this content, we will explore how to develop a booking system using Prisma as an ORM and following good software architecture practices.
The booking controller is the component that handles HTTP requests related to appointments or reservations. To keep a clean and maintainable code, it is advisable to follow a consistent pattern for each CRUD (Create, Read, Update, Delete) operation.
A well-structured controller should:
For updating reservations, the controller should verify the existence of the resource before attempting to modify it:
exports.updateReservation = async (req, res) => { try { const reservation = await reservationService.updateReservation(req.params.id, req.body); if (!reservation) { return res.status(404).json({ error: "Reservation not found" }); } return res.status(200).json(reservation); } catch (error) { return res.status(400).json({ error: error.message }); } } };
Similarly, to delete a reservation, it is crucial to verify that the resource exists:
exports.deleteReservation = async (req, res) => { try { const reservation = await reservationService.deleteReservation(req.params.id); if (!reservation) { return res.status(404).json({ error: "Reservation not found" }); } return res.status(204).send(); } catch (error) { return res.status(400).json({ error: error.message }); } } };
Separation of responsibilities is a fundamental principle in software development. By moving business logic into dedicated services, we achieve:
To implement this architecture, we create a reservationService.js
file inside the services folder:
const prisma = require('../path/to/prisma');
exports.createReservation = async (data) => { // Check if the schedule is already taken const conflict = await prisma.appointment.findFirst({ where: { date: data.date, timeBlockId: data.timeBlockId } } });
if (conflict) { throw new Error("The schedule is already taken"); }
return prisma.appointment.create({ data });};
One of the most important parts of a reservation system is to avoid schedule conflicts. We do not want two users to be able to book the same schedule, which would generate operational problems.
To implement this validation, we must check if a reservation already exists for the requested date and time block:
exports.updateReservation = async (id, data) => { // Check if the new schedule is already occupied by another reservation const conflict = await prisma.appointment.findFirst({ where: { date: data.date, timeBlockId: data.timeBlockId, id: { not: parseInt(id, 10) } } } });
if (conflict) { throw new Error("The requested schedule is already occupied"); }
return prisma.appointment.update({ where: { id: parseInt(id, 10) }, data });};
Notice how in the validation to update, we exclude the current booking from the conflict check using id: { not: parseInt(id, 10) }.
This allows a reservation to be updated without changing its schedule without generating a false conflict.
Query and delete operations are simpler, as they do not require complex conflict validations:
exports.getReservation = async (id) => { return prisma.appointment.findUnique({ where: { id: parseInt(id, 10) } } } );}; };
exports.deleteReservation = async (id) => { return prisma.appointment.delete({ where: { id: parseInt(id, 10) } } } );};
Once the system is implemented, it is crucial to test it to ensure its correct functioning. Tools such as Postman are ideal for testing REST APIs:
It is important to verify:
When testing the GET endpoint to obtain a reservation, we must make sure to include the authentication token in the headers:
Authorization: Bearer [tu_token_jwt]
If we forget to include the token, we will receive an access denied error: "Access denied: No token provided".
The structure of our system allows us to easily detect errors. For example, if we forget to import the service in the controller, we will see an error like "reservation service is not defined", which tells us exactly what we need to correct.
Developing a robust reservation system requires attention to detail and a well thought-out architecture. Separating responsibilities between controllers and services helps us to maintain a clean and maintainable code in the long run. What is your preferred approach to structuring your services? Do you use individual exports or do you prefer an object with multiple methods? Share your experience in the comments.
Contributions 0
Questions 0
Want to see more contributions, questions and answers from the community?