You don't have access to this class

Keep learning! Join and start boosting your career

Aprovecha el precio especial y haz tu profesi贸n a prueba de IA

Antes: $249

Currency
$209
Suscr铆bete

Termina en:

0 D铆as
6 Hrs
11 Min
14 Seg
Curso de Backend con ExpressJS

Curso de Backend con ExpressJS

Oscar Barajas Tavares

Oscar Barajas Tavares

Reservations parte 2

27/30
Resources

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.

How to structure an efficient reservation controller?

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:

  • Receive and validate the request parameters.
  • Delegate business logic to services
  • Properly handle errors
  • Return responses with appropriate HTTP codes

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 }); } } };

Why separate logic into services?

Separation of responsibilities is a fundamental principle in software development. By moving business logic into dedicated services, we achieve:

  • Greater modularity and code reuse.
  • Ease of unit testing
  • Better long-term maintainability
  • Cleaner and more understandable code

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 });};

How to implement conflict validation in reservations?

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.

How to implement query and delete operations?

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) } } } );};

How to test our reservation system?

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:

  1. Import the endpoint collection into Postman.
  2. Configure authentication headers (using JWT tokens)
  3. Test each endpoint with different scenarios

It is important to verify:

  • That the conflict validations are working correctly
  • That HTTP status codes are appropriate
  • Authentication and authorization work as expected.

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

Sort by:

Want to see more contributions, questions and answers from the community?