Base URL: http://localhost:8080/api/v1
All responses use the following envelope:
{ "success": true, "data": { ... } }
{ "success": false, "error": "reason" }GET /health
Response 200
{ "status": "ok" }POST /api/v1/recipes
Content-Type: multipart/form-data
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | ✅ | Recipe title |
description |
string | ❌ | Short description |
instructions |
string | ✅ | Step-by-step instructions |
user_id |
string | ✅ | Creator's user ID |
ingredient_names[] |
string[] | ❌ | Ingredient names (repeated) |
ingredient_qtys[] |
string[] | ❌ | Quantities (positional match) |
image |
file | ❌ | JPEG/PNG image |
cURL Example
curl -X POST http://localhost:8080/api/v1/recipes \
-F "title=Spaghetti Carbonara" \
-F "description=Classic Italian pasta dish" \
-F "instructions=1. Boil pasta. 2. Mix eggs and cheese. 3. Combine with pancetta." \
-F "user_id=user-abc-123" \
-F "ingredient_names[]=spaghetti" \
-F "ingredient_qtys[]=200g" \
-F "ingredient_names[]=eggs" \
-F "ingredient_qtys[]=3 large" \
-F "ingredient_names[]=pecorino romano" \
-F "ingredient_qtys[]=100g" \
-F "image=@./pasta.jpg"Response 201
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Spaghetti Carbonara",
"description": "Classic Italian pasta dish",
"instructions": "1. Boil pasta...",
"image_path": "./uploads/3f2504e0-4f89-11d3-9a0c-0305e82c3301.jpg",
"user_id": "user-abc-123",
"ingredients": [
{ "ingredient_id": 1, "quantity": "200g", "ingredient": { "id": 1, "name": "spaghetti" } }
],
"created_at": "2026-02-20T12:00:00Z",
"updated_at": "2026-02-20T12:00:00Z"
}
}GET /api/v1/recipes?page=1&limit=10
Query Params
| Param | Default | Max | Description |
|---|---|---|---|
page |
1 | — | Page number |
limit |
10 | 100 | Items per page |
cURL Example
curl "http://localhost:8080/api/v1/recipes?page=1&limit=5"Response 200
{
"success": true,
"data": [ { "id": "...", "title": "...", "avg_rating": 4.5, ... } ],
"meta": {
"current_page": 1,
"per_page": 5,
"total_items": 23,
"total_pages": 5
}
}GET /api/v1/recipes/:id
cURL Example
curl http://localhost:8080/api/v1/recipes/550e8400-e29b-41d4-a716-446655440000Response 200 — full recipe with ingredients, ratings, and avg_rating
Response 404 — { "success": false, "error": "recipe not found" }
DELETE /api/v1/recipes/:id
cURL Example
curl -X DELETE http://localhost:8080/api/v1/recipes/550e8400-e29b-41d4-a716-446655440000Response 204 — No Content
Response 404 — { "success": false, "error": "recipe not found" }
GET /api/v1/recipes/search?ingredients=<comma-separated>&page=1&limit=10
Returns recipes that contain all specified ingredients (AND logic).
cURL Example
curl "http://localhost:8080/api/v1/recipes/search?ingredients=spaghetti,eggs&page=1&limit=10"Response 200 — same paginated envelope as List Recipes
POST /api/v1/recipes/:id/rate
Content-Type: application/json
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
user_id |
string | ✅ | Rater's user ID |
score |
int | ✅ | Rating score (1–5) |
cURL Example
curl -X POST http://localhost:8080/api/v1/recipes/550e8400-e29b-41d4-a716-446655440000/rate \
-H "Content-Type: application/json" \
-d '{"user_id": "user-xyz-456", "score": 5}'Response 200
{
"success": true,
"data": {
"your_score": 5,
"avg_rating": 4.7
}
}Response 400 — { "success": false, "error": "score must be between 1 and 5" }
Response 404 — { "success": false, "error": "recipe not found" }
A user can update their rating by submitting again — the score will be overwritten (upsert behavior).
| HTTP Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No Content (delete) |
| 400 | Bad Request / Validation failure |
| 404 | Resource not found |
| 500 | Internal Server Error |