Backend Application Project — Diet Detective🥚🥜🧈

Nicholene Davison
6 min readNov 19, 2023

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.

Class UML for the Dining Review API

📏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

1. Let’s add two users
2. Get one user by username, verify information
3. Update one user’s information and get that user to verify information changed

🍱PART 2: THE RESTAURANTS

1. Create 1st restaurant
2. Verify restaurant is in the database
3. Create 2nd restaurant
4. Verify restaurant is in the database
5. Get all restaurants
6. Return restaurants by zipcode and allergy

📢PART 3: THE REVIEWS

1. Create a user review using Restaurant 1 and User Nicholene
2. Get the status of all reviews
3. Approve review 1
4. Get the status of accepted reviews
5. Get restaurant 1→ See that the rating of Restaurant 1 is based on only one review
6. Now time to add a second review for restaurant 1
7. Now approve the second review for the restaurant 1
8. Now let’s verify the rating for restaurant 1 changed → SUCCESS!🥳

💯 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:

Example Screenshot of AdminService
Example Screenshot of AdminController

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:

See the @Autowired annotation on the repositories

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.💞

--

--

Nicholene Davison

Hello there, I am an aspiring software engineer with an interest in cybersecurity. I hope to share thoughts that could be helpful to you.