Skip to main content
Dat 3rd Sem Fall 2025
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Fall 2025 re

Backend Re-Exam Assignments Fall 2025

This document is an exam assignment for 3rd semester Datamatiker students at EK. After submission, the code in the main branch must no longer be changed.

Practicalities

  • Allowed resources: written materials, personal computers, laptops, extra monitors, and internet resources. Headphones, and listening to music.
  • Prohibited: communication with anyone. So no use of social media, forums, emails, SMS, chatrooms, etc.
  • Do not store solutions on external networks or drives/hosts like Facebook, OneDrive, Google Drive, etc. And don’t share your code on Github until the end of the exam.
  • Duration: 5 hours.

Hand in on Wiseflow

Upload a document (.txt or .pdf) to Wiseflow with:

  • A link to your public GitHub repository. Remember to push your solutions at the very end of the exam. If you push it before that, then make sure the repository is private until hand-in. Do not copy the clone link from GitHub, but grab the link from the browser address bar and paste it into a document and upload to Wiseflow.

Background

The Recipe Planner Application is a backend system for a cooking and meal-prep platform helping users manage recipes and ingredients. Users can browse, create, update, and delete recipes and manage their ingredients, and view their nutritional insights.

The system exposes REST endpoints via Javalin, uses JPA/Hibernate for persistence, integrates with an external Nutrition Stats API, and includes JWT-based authentication and REST testing.


Non-Functional Requirements

CategoryRequirement
Architecture & TechnologyThe backend must be built using Java, Javalin, and JPA/Hibernate.
Data PersistenceEntities and DAOs must use JPA annotations and connect through a Hibernate configuration.
DTO UsageAll REST communication must use DTOs to decouple internal entities from exposed data.
API DesignThe system must expose RESTful endpoints that conform to standard HTTP methods (GET, POST, PUT, DELETE).
Error Handling and ValidationAll exceptions must be returned as structured JSON objects with appropriate HTTP status codes. Validation of input must be demonstrated and return meaningful error messages.
External IntegrationThe system must consume an external Nutrition Stats API to enrich recipes’ ingredients with nutritional data.
TestingEach critical REST endpoint must be tested using JUnit and Rest Assured, including data setup and teardown.
SecurityThe system must implement JWT authentication, support role-based access control, and secure all write endpoints (POST, PUT, DELETE) with JWT and roles.
DocumentationThe system must include a clear README.md with a status overview of how far you have come, and notes about any important design decisions.
MaintainabilityCode should be modular, with a clean separation between layers (Controller, DAO, DTO, Entity, Routes).
ReliabilityUnit and integration tests must be performed for all critical business logic and API endpoints.
Data IntegrityDeleting a recipe or ingredient must handle cascading relationships correctly and safely.

User Stories

US-1: As a developer

I want to configure the database connection and entity management using Hibernate
so that the application can persist and retrieve recipe and ingredient data reliably.

  • Each recipe has a title, description, and category (enum).
  • Each ingredient has a name, type (enum), and description. It also has a unique slug (String) used for matching with the external Nutrition Stats API.
  • Each recipe can have many ingredients, and each ingredient can belong to many recipes.
  • The relationship between recipes and ingredients is many-to-many, implemented via a join table RecipeIngredient.
  • The join table should include additional fields: quantity (an integer), unit (String), and preparation (e.g., “chopped”, “diced”). In this way you would be able to specify how much of each ingredient is needed for a recipe and how it should be prepared. Like so: 2 dl of milk, chopped onions, etc.

Hint! Because the join table has additional fields, you will need to create a separate entity for it (i.e., RecipeIngredient), and use @ManyToOne relationships from RecipeIngredient to both Recipe and Ingredient.

Acceptance Criteria

  • The system must include entities for Recipe, Ingredient, and the join table RecipeIngredient.
  • The system must initialize with sample data via a Populator class that is run once at application startup.

Suggested Recipe Categories (enum) and examples:

Enum valueDescription
BREAKFASTMorning meals
LUNCHMidday meals
DINNERMain meals
DESSERTSweet dishes
SNACKSmall/light meals

Suggested Ingredient Types (enum) and examples:

Enum valueExample ingredientsDescription
VEGETABLEOnion, Garlic, Carrot, SpinachPlant-based edible vegetables
OILOlive OilCooking oils and fats derived from plants
DAIRYButter, MilkMilk-based products
PROTEINEgg, Chicken Breast, BeefHigh-protein animal-based foods
SEASONINGSalt, Black Pepper, Soy SauceIngredients used primarily for flavoring
GRAINRice, PastaGrain-based staple foods
SWEETENERSugar, HoneyIngredients used to add sweetness
FRUITLemon, LimeEdible fruits with sweet or sour flavors
HERBBasil, Cilantro, ParsleyFresh green herbs used for aroma and flavor
CONDIMENTTomato SauceSauces or toppings added to enhance dishes
BAKINGYeast, Baking PowderIngredients used for baking and dough preparation

The Populator must create at least:

  • 2–3 recipes
  • 4–6 ingredients
  • At least 1 recipe with linked ingredients via RecipeIngredient

In case you need inspiration for samlple recipe data, a few are listed below in Appendix A .


US-2: As a developer

I want to create, read, update, and delete recipe and ingredient records through DAOs
so that I can manage data consistently across the application.

Acceptance Criteria

  • RecipeDAO implements CRUD and allows linking recipes and ingredients.
  • IngredientDAO implements basic CRUD as needed.
  • DTOs are used for all data exchange between layers.

US-3: As a REST API consumer

I want to manage recipes using HTTP endpoints
so that I can perform standard CRUD operations and attach ingredients to recipes.

Note: You are not required to implement separate REST endpoints for ingredients. Ingredients can be created via the Populator or directly in the database.

Acceptance Criteria

Recipe CRUD

  • GET /recipes. Returns all recipes.

  • GET /recipes/{id}. Returns a recipe including:

    • its base fields (title, description, category)
    • all attached ingredients with their quantity, unit, and preparation
  • POST /recipes. Creates a new recipe.

  • PUT /recipes/{id}. Updates an existing recipe.

  • DELETE /recipes/{id}. Deletes a recipe and its associated join entities (RecipeIngredient).

Add an ingredient to a recipe (create join entity)

  • POST /recipes/{recipeId}/ingredients. Creates a RecipeIngredient entry for the given recipe.

    Request body:

    {
      "ingredientId": 5,
      "quantity": 2,
      "unit": "dl",
      "preparation": "finely chopped"
    }
    

    The endpoint must:

    • validate that the recipe exists
    • validate that the ingredient exists
    • Handling duplicate ingredient links is not required. You may assume that each ingredient is added to a recipe only once.
    • create a RecipeIngredient entity
    • store quantity, unit, and preparation
    • attach it to the correct recipe

    Return value

    • the created RecipeIngredient DTO. Returning the updated RecipeDTO is also acceptable.

US-4: As a meal planner

I want to view and filter recipes by recipe category
so that I can find recipes that match specific meal types.

Acceptance Criteria

  • GET /recipes?category={category} filters recipes based on their category using JPA or streams.

US-5: As a nutrition-aware user

I want to see nutrition insights (calories, protein, fat, carbs) for each recipe’s ingredients
so that I can assess the nutritional value of each recipe.

Acceptance Criteria:

  • When retrieving a recipe by ID, the response must include enriched ingredient data fetched from the external Nutrition Stats API.
  • Each ingredient in the response must include:
    calories, protein, fat, and carbs.
  • If the recipe has no ingredients, enrichment returns an empty list.
  • If an ingredient is unknown in the external API, it is returned without enrichment data.

Details about the external Nutrition Stats API

  • The external API is available at
    https://apiprovider.cphbusinessapps.dk/api/v1/ingredients/nutrition?slugs=onion,garlic,olive-oil
    where multiple ingredient slugs can be passed as a comma-separated list in the slugs query parameter.

  • The Ingredient entity has a slug field (e.g., “onion”, “garlic”, “olive-oil”) used to match with the external API.

  • The API returns a JSON object with nutrition data for each ingredient in this format:

[
  {
    "id": "4a",
    "slug": "onion",
    "name": "Onion",
    "categoryKey": "vegetable",
    "calories": 40,
    "protein": 1.1,
    "fat": 0.1,
    "carbs": 9.3,
    "updatedAt": "2025-10-01T10:15:00.000Z"
  },
  {
    "id": "5a",
    "slug": "garlic",
    "name": "Garlic",
    "categoryKey": "vegetable",
    "calories": 149,
    "protein": 6.4,
    "fat": 0.5,
    "carbs": 33,
    "updatedAt": "2025-10-01T10:15:00.000Z"
  },
  {
    "id": "6a",
    "slug": "olive-oil",
    "name": "Olive Oil",
    "categoryKey": "oil",
    "calories": 884,
    "protein": 0,
    "fat": 100,
    "carbs": 0,
    "updatedAt": "2025-10-01T10:15:00.000Z"
  }
]

Hint: The external API can be called once per recipe, passing multiple slug parameters.

NB: Each ingredient also has a slug, a short, unique identifier (e.g. “garlic”), used to match local ingredients with data from the external API.

NB: The date format for updatedAt is ZonedDateTime format, e.g. 2025-10-01T10:15:00.000Z. Jackson might need an extra dependency to handle this format and this custom configuration of the ObjectMapper:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

US-6: As a tester

I want automated tests for all critical REST endpoints so that the application’s functionality is verified and regressions are avoided.

Acceptance Criteria:

  • The most important success and failure paths are tested (not necessarily every tiny endpoint)
  • Tests set up mock data and verify JSON responses and status codes.
  • Recipe-by-ID tests confirm enrichment data is included.

US-7: As a secure API consumer

I want to log in and access protected endpoints using a JWT token so that only authorized users can modify sensitive data.

Acceptance Criteria:

  • POST /login authenticates and returns a JWT.
  • POST /register creates a user with username, password, and role.
  • Secure all write endpoints (POST, PUT, DELETE) with JWT and roles.
  • Protected endpoints validate the token and enforce roles.
  • Unauthorized requests return 401 Unauthorized.
  • Tests verify secure access behavior.

Guiding Grading Criteria

DimensionPointsWhat to consider
REST design & correctness20Endpoints, Controller, status codes, DTO shapes
Data model & JPA mapping20Relations, cascading, constraints
Nutrition Stats integration15Correct call, enrichment logic, inclusion in responses
Security (JWT + roles)10Add roles to endpoints, adjust REST Assured tests to JWT
Testing20Coverage of success & failure paths, isolation
Error handling & validation10Exception handling, Consistent JSON errors, field errors
Code quality & README5Structure, clarity, how-to-run
Total100

Appendix A: Sample Recipe Data

1. Garlic Scrambled Eggs

Ingredients

  • 2 eggs — beaten
  • 10 g butter — melted in the pan
  • 1 clove garlic — finely chopped
  • 1 pinch salt — to taste

2. Lemon Rice

Ingredients

  • 150 g rice — cooked
  • 1 tbsp olive oil — heated in the pan
  • ½ lemon — juice + a little zest
  • 1 pinch salt — to taste

3. Butter Basil Pasta

Ingredients

  • 200 g pasta — cooked al dente
  • 20 g butter — melted
  • 5 g fresh basil — roughly torn
  • 1 pinch black pepper — freshly ground