Summary
Implementing a REST API Endpoint for Dish Creation
Highlights
The process begins by creating a new 'DishesController' to conform to REST architecture. This controller will have a route attribute '/api/restaurant/{restaurantId}/dishes' and will inherit from the controller base class, similar to the existing 'RestaurantsController'. It will also have the 'ApiController' attribute applied.
A public async task of 'IActionResult' named 'CreateDish' is implemented. This method will accept two primary pieces of information: the 'restaurantId' from the route parameters and a 'CreateDishCommand' from the request body, which will represent the new dish entity. The 'HttpPost' attribute is applied to this endpoint.
The 'CreateDishCommand' is defined as a class within the application module, mimicking the structure of other commands. It inherits from the 'IRequest' interface from MediatR and includes properties like 'Name', 'Description', 'Price', and 'Kilocalories' to capture dish details. The 'restaurantId' is later added to this command for associating the dish with the correct restaurant.
A validator, 'CreateDishCommandValidator', is created for the 'CreateDishCommand' using FluentValidation. This validator ensures that properties like 'Price' and 'Kilocalories' are non-negative numbers, with custom error messages defined for invalid inputs.
The 'CreateDishCommandHandler' implements 'IRequestHandler<CreateDishCommand>'. It first logs the creation request. It then checks for the existence of the specified restaurant in the database using 'IRestaurantRepository'. If the restaurant is not found, a 'NotFoundException' is thrown. If found, the command is mapped to a 'Dish' entity, and the new dish is added to the database.
To maintain good practice and separate repository concerns, a new interface, 'IDishesRepository', is introduced. This interface defines a 'Create' method for adding 'Dish' entities. A concrete implementation, 'DishesRepository', is created in the infrastructure layer, using 'RestaurantsDbContext' to persist the dish data.
The 'IMapper' interface is injected into the 'CreateDishCommandHandler' to map the 'CreateDishCommand' to a 'Dish' entity. A mapping profile is added to AutoMapper to facilitate this conversion. Crucially, the 'IDishesRepository' is registered with the dependency injection container in the 'ServiceCollectionExtensions' to ensure its proper resolution.
Before running the application, it's identified that the 'restaurantId' from the route needs to be manually assigned to the 'CreateDishCommand' as it's not automatically bound. Testing with an API client demonstrates the validation effectively, returning errors for missing or invalid data. The process also includes testing with existing and non-existent restaurant IDs to confirm proper error handling.