Backend Application Project — Diet Detective🥚🥜🧈
A Java Spring, MVC, Maven, RESTful API, with H2 database
Leveraging the knowledge I acquired from my Java Spring API course on Codecademy, Coding with Mosh, and Study.com, I embarked on creating this project named ‘Diet Detective’. The primary aim of this project is to evaluate and rank restaurants based on the allergy-friendliness of their menus and service. In this article, I will delve into the following aspects:
- A Project Overview.
- How to use the project with an example scenario.
- The lessons I learned.
Project Overview🚧
Users can submit dining reviews on a restaurant based on egg, peanut, and/or dairy allergies. The review scores are added to the restaurant once an Admin approves the review. The restaurant scores are calculated as an average of all approved reviews.
For example, AppUser Steve submits a review of 5 for peanut, 5 for egg, and 5 for dairy for the restaurant PizzaPalace. Then AppUser Cruz submits a review of 1 for peanut, 1 for egg, and 3 for dairy for the same restaurant. Both reviews are approved by an Admin. Then the final scores for PizzaPalace would be 3.5 (Overall), 3.0 (peanut), 3.0 (egg), and 4.5 (dairy).
Communication with the API is done with cURL and JSON format. Perhaps I will add a front-end in the future to make the project more user-friendly.
🪵Structure
There are four entities, one enumeration, four services, and four controllers. I built separate controllers and services for each entity. This helps with the separation of concerns as each controller and service focuses on a specific entity, making the code easier to understand and maintain. Also, I like the concept of thin controllers. This is why I have services that handle the business logic where the controllers focus primarily on HTTP requests. See the Class UML diagram below for more information on the structure.
📏Validation
The validation is in the services. Some examples of validation include:
- The user and restaurant exist for the dining review that is being submitted.
- No score is given in a dining review.
- The zip code for restaurants has 5 digits.
🧮The H2 Database
An in-memory SQL database, H2, is used. The H2 .jar file is provided in the project for additional interaction if desired. JPA is used with the database to enable CRUD operations through the three repo interfaces. The data is not persistent and will reset to nothing when the application is restarted.
🪝How to Use the Project:
The following are all the commands organized by user, restaurant, review, and admin.
👤User Section
Create a user:
curl -X POST http://localhost:8080/users ^ -H "Content-Type: application/json" ^ -d "{ \"userName\": \"insert_name\", \"city\": \"insert_city\", \"state\": \"insert_state\", \"zipCode\": \"insert_5_digit_zipcode\", \"peanutAllergies\": true, \"eggAllergies\": true, \"dairyAllergies\": true }"
Update a user (add the parameters you want to change):
curl -X PUT -H "Content-Type: application/json" -d "{\"userName\":\"insert_name\", \"city\":\"insert_city\", \"state\":\"insert_state\"}" “http://localhost:8080/users/insert_name”
Get User information by AppUser name:
curl -X GET “http://localhost:8080/users/insert_name”
🌶️Restaurant Section
Create a new restaurant:
curl -X POST "http://localhost:8080/api" -H "Content-Type: application/json" -d "{\"name\": \"insert_restaurant_name\", \"lineOne\": \"insert_lineOneAddress\", \"city\": \"insert_city\", \"state\": \"insert_state\", \"zipCode\": \"insert_5_digit_zipcode\", \"phoneNumber\": \"insert_phonenumber\", \"website\": \"insert_URL\", \"rating\": \"insert_number_or_leave_blank\", \"peanutRating\": \"insert_number_or_leave_blank\", \"eggRating\": \"insert_number_or_leave_blank\", \"dairyRating\": \"insert_number_or_leave_blank\"}"
Get a restaurant by ID:
curl -X GET "http://localhost:8080/api/insert_id(integer)"
Get all restaurants:
curl -X GET "http://localhost:8080/api"
Get restaurants by zipcode and allergy:
curl -X GET "http://localhost:8080/api/search?zipcode=insert_5_digit_zipcode&allergy=insert_peanut||egg||diary"
✉️Review Section
Add a dining review:
curl -X POST "http://localhost:8080/admin" -H "Content-Type: application/json" -d "{\"name\": \"AppUser_name\", \"restaurantId\": insert_id(integer), \"comments\": \"insert_comments_here\", \"peanutScore\": insert_number, \"eggScore\": insert_number, \"dairyScore\": insert_number_or_leave_blank}"
🧑🏫Admin Section
Get all reviews by status (PENDING, ACCEPTED, REJECTED)
curl -X GET "http://localhost:8080/admin/reviews?review_status=PENDING" -H "Content-Type: application/json"
Get a review by ID
curl -X GET "http://localhost:8080/admin/reviews/insert_id(integer)”
Approve a review based on review ID (true = approved, false = rejected)
curl -X PUT "http://localhost:8080/admin/reviews/3" -H "Content-Type: application/json" -d "{\"adminApproved\": true}"
The following scenario with the API in action.
👩👨PART 1: THE USERS
🍱PART 2: THE RESTAURANTS
📢PART 3: THE REVIEWS
💯 Lessons I Learned
This project was undoubtedly the most challenging one I’ve tackled to date, but it was also a tremendous learning experience. Here’s a key takeaway that I hope will be of value to you:
1. Always remember to check 🕵️♀️ the application logs when encountering errors.
During this project, every time I tried to approve a dining review or retrieve a review, I was met with an error in the CLI (Windows Command Prompt)
{
"timestamp":"2023-11-17T22:48:13.763+00:00",
"status":500,
"error":"Internal Server Error",
"path":"/admin/reviews/2"
}
The error code 500 indicated that the issue was likely with my request validation. This code typically signifies a bad request, triggered when one of the validation criteria isn’t met. See some of the validation criteria below:
In my quest to resolve the persistent error, I meticulously commented out each part of the validation. Yet, the error persisted unless all validation was removed. What was causing this stubborn error? Frustration mounted as I grappled with the elusive internal server error. I scrutinized my code, and revisited the validation logic, but to no avail. After an hour of being stuck in this problem, I decided to take a break.
Returning an hour later with a refreshed mind, I approached the problem anew. It then dawned on me to check the application logs in the IDE, something I usually do but had overlooked while interacting with the application via the CLI (Windows Command Prompt).
“com.example.demo.repository.DiningReviewRep.findDiningReviewsByStatusAndRestaurantId(com.example.demo.model.ReviewStatus, java.lang.Long)”
because “this.reviewRepository” is null.
That was it!🎉🎉🎉
The @Autowired annotation for the repositories was missing in the AdminService. The NullPointerException was occurring because the instances of DiningReviewRep and RestaurantRep were not initialized in the AdminService. By adding the @Autowired annotation, Spring automatically injects the DiningReviewRep and RestaurantRep beans into the AdminService class, resolving the issue. See fix below:
2. Take breaks when frustrated. 🧘♀️
Taking breaks when feeling stuck or frustrated is highly beneficial. It provides an opportunity to step back, refresh the mind, and approach problems from a new perspective. Continuous work can often lead to tunnel vision, hindering creativity and problem-solving abilities. Breaks can also prevent burnout, improve mental well-being, and boost productivity. Moreover, they can be used for learning, self-improvement, or even casual coding, which can indirectly contribute to problem-solving. So, remember to take a pause, breathe, and return to your task with renewed energy and a fresh mindset. This helped me.
💭Final Thoughts💭:
As for this project, it’s my first venture into creating “personal” projects. I’m eager to hear your thoughts and feedback. Your insights will be invaluable in helping me grow professionally.💞