- IDE / code editor
- SQLite compatible database GUI
- API testing client tool (Postman, HTTPie, ...)
npm install
npm run prisma:generate
npm run dev
open http://localhost:3000
npm test
Defaulted in watch mode
Important
- Please turn off any AI helping tool.
- For the entire test, you are asked to improve the architecture of the codebase to enhance readability and provide the best solutions for each exercise.
- We don't expect you to know prisma (ORM), feel free to ask whatever you want about it.
- For simplicity purpose, we don't expect any operation to be transactional.
Timing This test is designed to be completed in approximately 45 minutes to 1 hour. Each part includes a time estimate to help you manage your time. If you run out of time on a part, explain in a comment what you would have done.
Evaluation criteria We evaluate the clarity of your code, your architectural choices, and your ability to justify them. We value quality over quantity — a well-structured partial solution is better than a rushed complete one.
Add rules to validate the format of the provided input before persisting it.
a. A Recipe must have at least 1 ingredient.
b. prepTime cannot be null or 0.
c. cookingTime cannot be null but can be 0.
Validation errors should be returned to the client in a clear and exploitable way.
Think about where in the architecture this validation logic should live.
Implement a unit test to verify recipe creation and persistence.
The test should not rely on any prisma related code.
The tested module should be the createRecipeUseCase, do not bother calling the endpoint.
The current code is not testable in isolation — you will need to refactor CreateRecipeUseCase to make it testable. This refactoring is expected and part of the exercise.
An interface RecipeRepository is already defined in src/repositories/recipe/index.ts. Write an InMemoryRecipeRepository that implements this interface and stores recipes in a simple array. Use it in your tests instead of mocking.
Your tests should cover:
- A successful recipe creation (the recipe is correctly persisted with all its data).
- Validation errors from part 1 (if you completed it).
We want to add a totalTime property to each recipe.
This property should be a computation such as
totalTime = prepTime + cookingTime.
Build a Recipe class so that each instance is guaranteed to be a valid recipe (with valid values).
- Implement the class and include
totalTimeas a computed property. - The class should enforce the validation rules from part 1 — it should be impossible to instantiate an invalid
Recipe. - Test the class behavior (valid creation, rejection of invalid data,
totalTimecomputation). - Update
CreateRecipeUseCaseto use theRecipeclass. You don't need to refactor the routes or the repository — focus on the domain and use case layers.
The recipe page (frontend/) is already split into components
(RecipePage / RecipeList / RecipeListItem / RecipeForm), and the
fetching is already extracted into useRecipes / useCreateRecipe
(frontend/hooks/) — you don't need to touch those.
RecipeForm (react-hook-form) currently does
everything in one place: the form wiring, the validation rules (inline in
register), the transformation of the form values into the API payload, and the
rendering.
The exercise is the refactoring. Pull this logic apart so the component is
easier to maintain — for instance a custom hook for the form
(state/validation/submit) and a place for the validation rules — and leave
RecipeForm focused on rendering. There is no single "right" decomposition;
make choices and own them.
A behaviour test is provided in frontend/recipeForm.test.tsx. It drives the
form through the DOM, so it asserts on what the user sees rather than on the
internals — it should stay green throughout your refactoring (treat it as a
safety net). The happy path is already written; fill in the it.todo cases for
the validation rules (title required, prepTime > 0, cookingTime >= 0, at
least one ingredient).
zod + @hookform/resolvers are installed if you want to
use a schema for the validation, but that's up to you.