docs: add OpenAPI documentation

This commit is contained in:
2025-08-12 17:29:02 +02:00
parent cd91ac11f9
commit aab7ff1452
25 changed files with 2422 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
summary: Account registration
value:
{
"email": "ada@example.com",
"password": "FBR5h0L1ENA3edctgDIEnxSvFdR0",
}

View File

@@ -0,0 +1,45 @@
summary: Dip category search
value:
[
{
"id": 3,
"name": "Muhammara",
"description": "Muhammara is a vibrant and flavorful dip or spread from the Middle East, particularly popular in Syrian and Turkish cuisine. It's made primarily from roasted red bell peppers, walnuts, breadcrumbs, and pomegranate molasses.",
"changedAt": "2025-08-08T16:24:44.673034Z",
"categories": [ "Dip" ],
"ingredients":
[
"Pomegranate molasses",
"Red bell peppers",
"Bread crumbs",
"Cumin",
"Chili flakes",
"Garlic",
"Walnuts",
"Lemon juice",
],
"steps":
[
"Roast whole bell peppers in an oven or air fryer and wait until blackened on the outside.",
"Put the peppers in a bowl cover them with to let steam and cool down",
"Remove core seeds, and skins",
"Combine all the ingredients in a food processor.",
"Process until mostly smooth but with a little texture remaining.",
"Taste and adjust flavor as needed (lemon for acidity, pomegranate molasses for sweetness)",
],
},
{
"id": 2,
"name": "Hummus",
"description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.",
"changedAt": "2025-08-08T16:24:44.579213Z",
"categories": [ "Dip" ],
"ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ],
"steps":
[
"Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.",
"Blend until smooth, slowly adding cold water until you reach a creamy consistency.",
"Taste and adjust seasoning if needed.",
],
},
]

View File

@@ -0,0 +1,15 @@
summary: Hummus recipe
value:
{
"name": "Hummus",
"description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.",
"categories": [ "Dip", "Spread" ],
"ingredients":
[ "Chickpeas", "Tahini", "Olive oil", "Lemon juice", "Garlic" ],
"steps":
[
"Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.",
"Blend until smooth, slowly adding cold water until you reach a creamy consistency.",
"Taste and adjust seasoning if needed.",
],
}

View File

@@ -0,0 +1,17 @@
summary: Hummus recipe
value:
{
"id": 1729,
"name": "Hummus",
"description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.",
"changedAt": "2025-08-08T15:35:10.294411Z",
"categories": [ "Dip", "Spread" ],
"ingredients":
[ "Chickpeas", "Tahini", "Olive oil", "Lemon juice", "Garlic" ],
"steps":
[
"Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.",
"Blend until smooth, slowly adding cold water until you reach a creamy consistency.",
"Taste and adjust seasoning if needed.",
],
}

View File

@@ -0,0 +1,14 @@
summary: Hummus recipe update
value:
{
"name": "Hummus",
"description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.",
"categories": [ "Dip", "Spread" ],
"ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ],
"steps":
[
"Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.",
"Blend until smooth, slowly adding cold water until you reach a creamy consistency.",
"Taste and adjust seasoning if needed.",
],
}

View File

@@ -0,0 +1,16 @@
summary: Hummus recipe update
value:
{
"id": 1729,
"name": "Hummus",
"description": "Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.",
"changedAt": "2025-08-09T12:48:32.134821Z",
"categories": [ "Dip", "Spread" ],
"ingredients": [ "Chickpeas", "Tahini", "Lemon juice", "Garlic" ],
"steps":
[
"Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.",
"Blend until smooth, slowly adding cold water until you reach a creamy consistency.",
"Taste and adjust seasoning if needed.",
],
}

24
openapi/index.html Normal file
View File

@@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<title>Recipe API Reference</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="app"></div>
<!-- Load the Script -->
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
<!-- Initialize the Scalar API Reference -->
<script>
Scalar.createApiReference("#app", {
// The URL of the OpenAPI/Swagger document
url: "/recipe-openapi.yaml",
hideTestRequestButton: true,
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
name: "recipeId"
in: "path"
required: true
schema:
$ref: "../schemas/Id.yaml"
description: "Unique identifier of the recipe"

View File

@@ -0,0 +1,23 @@
post:
tags:
- Account
summary: "Register an account"
operationId: "RegisterAccount"
requestBody:
content:
application/json:
schema:
$ref: "../schemas/Account.yaml"
examples:
accountIn:
$ref: "../examples/AccountIn.yaml"
required: true
responses:
"200":
description: "OK"
"400":
description: "Bad Request"
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"

View File

View File

@@ -0,0 +1,50 @@
post:
tags:
- Recipe
summary: "Add a recipe"
operationId: "addRecipe"
requestBody:
content:
application/json:
schema:
$ref: "../schemas/Recipe.yaml"
examples:
recipeIn:
$ref: "../examples/HummusV1In.yaml"
required: true
responses:
200:
description: OK
content:
"application/json":
schema:
$ref: "../schemas/Recipe.yaml"
examples:
recipeOut:
$ref: "../examples/HummusV1Out.yaml"
400:
description: Bad Request
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
401:
description: Unauthorized
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
403:
description: Forbidden
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
404:
description: Not Found
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
security:
- httpBasic: [ ]

View File

@@ -0,0 +1,59 @@
get:
tags:
- Recipe
summary: "Find recipes"
operationId: "FindRecipes"
parameters:
- name: "category"
in: "query"
required: false
schema:
type: "string"
description: "Category name filter (equals, ignores case)"
- name: "recipe"
in: "query"
required: false
schema:
type: "string"
description: "Recipe name filter (contains, ignores case)"
- name: "limit"
in: "query"
required: true
schema:
type: "integer"
format: "int64"
minimum: 1
maximum: 100
description: "Maximum number of recipes to return"
- name: "offset"
in: "query"
required: true
schema:
type: "integer"
format: "int64"
minimum: 0
description: "Index into result set from where recipes will start to be returned"
responses:
200:
description: OK
content:
"application/json":
schema:
type: "array"
items:
$ref: "../schemas/Recipe.yaml"
examples:
dipsOut:
$ref: "../examples/DipsOut.yaml"
400:
description: Bad Request
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
404:
description: Not Found
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"

View File

@@ -0,0 +1,117 @@
get:
tags:
- Recipe
summary: "Get a recipe"
operationId: "GetRecipe"
parameters:
- $ref: "../parameters/{recipeId}.yaml"
responses:
200:
description: OK
content:
"application/json":
schema:
$ref: "../schemas/Recipe.yaml"
examples:
recipeOut:
$ref: "../examples/HummusV1Out.yaml"
400:
description: Bad Request
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
404:
description: Not Found
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
put:
tags:
- Recipe
summary: "Update a recipe"
operationId: "UpdateRecipe"
parameters:
- $ref: "../parameters/{recipeId}.yaml"
requestBody:
content:
application/json:
schema:
$ref: "../schemas/Recipe.yaml"
examples:
recipeIn:
$ref: "../examples/HummusV2In.yaml"
required: true
responses:
200:
description: OK
content:
"application/json":
schema:
$ref: "../schemas/Recipe.yaml"
examples:
recipeOut:
$ref: "../examples/HummusV2Out.yaml"
400:
description: Bad Request
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
401:
description: Unauthorized
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
403:
description: Forbidden
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
404:
description: Not Found
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
security:
- httpBasic: [ ]
delete:
tags:
- Recipe
summary: "Remove a recipe"
operationId: "RemoveRecipe"
parameters:
- $ref: "../parameters/{recipeId}.yaml"
responses:
200:
description: OK
400:
description: Bad Request
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
401:
description: Unauthorized
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
403:
description: Forbidden
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
404:
description: Not Found
content:
"application/json":
schema:
$ref: "../schemas/ProblemDetail.yaml"
security:
- httpBasic: [ ]

View File

@@ -0,0 +1,497 @@
openapi: 3.1.1
info:
title: Recipe API
description: Programmatic access to a database of recipes. Users can publish and find recipes.
version: 1.0.0
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.recipes.bitfield.eu/v1
security: []
tags:
- name: Recipe
description: Steps and ingredients for preparing a dish
- name: Account
description: Access to protected operations
paths:
/recipe/new:
post:
tags:
- Recipe
summary: Add a recipe
operationId: addRecipe
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Recipe'
examples:
recipeIn:
$ref: '#/components/examples/HummusV1In'
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Recipe'
examples:
recipeOut:
$ref: '#/components/examples/HummusV1Out'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
security:
- httpBasic: []
/recipe/search:
get:
tags:
- Recipe
summary: Find recipes
operationId: FindRecipes
parameters:
- name: category
in: query
required: false
schema:
type: string
description: Category name filter (equals, ignores case)
- name: recipe
in: query
required: false
schema:
type: string
description: Recipe name filter (contains, ignores case)
- name: limit
in: query
required: true
schema:
type: integer
format: int64
minimum: 1
maximum: 100
description: Maximum number of recipes to return
- name: offset
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
description: Index into result set from where recipes will start to be returned
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Recipe'
examples:
dipsOut:
$ref: '#/components/examples/DipsOut'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
/recipe/{recipeId}:
get:
tags:
- Recipe
summary: Get a recipe
operationId: GetRecipe
parameters:
- $ref: '#/components/parameters/{recipeId}'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Recipe'
examples:
recipeOut:
$ref: '#/components/examples/HummusV1Out'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
put:
tags:
- Recipe
summary: Update a recipe
operationId: UpdateRecipe
parameters:
- $ref: '#/components/parameters/{recipeId}'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Recipe'
examples:
recipeIn:
$ref: '#/components/examples/HummusV2In'
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Recipe'
examples:
recipeOut:
$ref: '#/components/examples/HummusV2Out'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
security:
- httpBasic: []
delete:
tags:
- Recipe
summary: Remove a recipe
operationId: RemoveRecipe
parameters:
- $ref: '#/components/parameters/{recipeId}'
responses:
'200':
description: OK
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
security:
- httpBasic: []
/account/register:
post:
tags:
- Account
summary: Register an account
operationId: RegisterAccount
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
examples:
accountIn:
$ref: '#/components/examples/AccountIn'
required: true
responses:
'200':
description: OK
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
components:
securitySchemes:
httpBasic:
type: http
scheme: basic
schemas:
Id:
type: integer
format: int64
NonBlankString:
type: string
pattern: ^(.|\s)*\S(.|\s)*$
DateTime:
type: string
format: date-time
Recipe:
type: object
properties:
id:
$ref: '#/components/schemas/Id'
readOnly: true
description: Unique identifier of the recipe
name:
$ref: '#/components/schemas/NonBlankString'
description: Name
description:
$ref: '#/components/schemas/NonBlankString'
description: Description
changedAt:
$ref: '#/components/schemas/DateTime'
readOnly: true
description: Last point in time when the recipe was changed
categories:
type: array
items:
$ref: '#/components/schemas/NonBlankString'
description: Category names
ingredients:
type: array
minItems: 1
items:
$ref: '#/components/schemas/NonBlankString'
description: Step descriptions
steps:
type: array
minItems: 1
items:
$ref: '#/components/schemas/NonBlankString'
description: Ingredient names
required:
- name
- description
- categories
- ingredients
- steps
ProblemDetail:
type: object
properties:
type:
type: string
description: URI reference that identifies the problem type.
format: uri
status:
type: integer
description: HTTP status code generated by the origin server for this occurrence of the problem.
format: int32
title:
type: string
description: Short, human-readable summary of the problem type. It should not change from occurrence to occurrence of the problem, except for purposes of localization.
detail:
type: string
description: Human-readable explanation specific to this occurrence of the problem.
instance:
type: string
description: URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
properties:
type: object
description: Map for additional, non-standard properties
required:
- title
Email:
type: string
format: email
description: Email address
Password:
type: string
format: password
minLength: 8
description: Password
Account:
type: object
properties:
email:
$ref: '#/components/schemas/Email'
password:
$ref: '#/components/schemas/Password'
required:
- email
- password
examples:
HummusV1In:
summary: Hummus recipe
value:
name: Hummus
description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.
categories:
- Dip
- Spread
ingredients:
- Chickpeas
- Tahini
- Olive oil
- Lemon juice
- Garlic
steps:
- Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.
- Blend until smooth, slowly adding cold water until you reach a creamy consistency.
- Taste and adjust seasoning if needed.
HummusV1Out:
summary: Hummus recipe
value:
id: 1729
name: Hummus
description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.
changedAt: '2025-08-08T15:35:10.294411Z'
categories:
- Dip
- Spread
ingredients:
- Chickpeas
- Tahini
- Olive oil
- Lemon juice
- Garlic
steps:
- Combine chickpeas, tahini, olive oil, lemon juice, garlic, and salt in a food processor.
- Blend until smooth, slowly adding cold water until you reach a creamy consistency.
- Taste and adjust seasoning if needed.
DipsOut:
summary: Dip category search
value:
- id: 3
name: Muhammara
description: Muhammara is a vibrant and flavorful dip or spread from the Middle East, particularly popular in Syrian and Turkish cuisine. It's made primarily from roasted red bell peppers, walnuts, breadcrumbs, and pomegranate molasses.
changedAt: '2025-08-08T16:24:44.673034Z'
categories:
- Dip
ingredients:
- Pomegranate molasses
- Red bell peppers
- Bread crumbs
- Cumin
- Chili flakes
- Garlic
- Walnuts
- Lemon juice
steps:
- Roast whole bell peppers in an oven or air fryer and wait until blackened on the outside.
- Put the peppers in a bowl cover them with to let steam and cool down
- Remove core seeds, and skins
- Combine all the ingredients in a food processor.
- Process until mostly smooth but with a little texture remaining.
- Taste and adjust flavor as needed (lemon for acidity, pomegranate molasses for sweetness)
- id: 2
name: Hummus
description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.
changedAt: '2025-08-08T16:24:44.579213Z'
categories:
- Dip
ingredients:
- Chickpeas
- Tahini
- Lemon juice
- Garlic
steps:
- Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.
- Blend until smooth, slowly adding cold water until you reach a creamy consistency.
- Taste and adjust seasoning if needed.
HummusV2In:
summary: Hummus recipe update
value:
name: Hummus
description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.
categories:
- Dip
- Spread
ingredients:
- Chickpeas
- Tahini
- Lemon juice
- Garlic
steps:
- Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.
- Blend until smooth, slowly adding cold water until you reach a creamy consistency.
- Taste and adjust seasoning if needed.
HummusV2Out:
summary: Hummus recipe update
value:
id: 1729
name: Hummus
description: Hummus, is a Levantine dip, spread, or savory dish made from cooked, mashed chickpeas blended with tahini, lemon juice, and garlic.
changedAt: '2025-08-09T12:48:32.134821Z'
categories:
- Dip
- Spread
ingredients:
- Chickpeas
- Tahini
- Lemon juice
- Garlic
steps:
- Combine chickpeas, tahini, lemon juice, garlic, and salt in a food processor.
- Blend until smooth, slowly adding cold water until you reach a creamy consistency.
- Taste and adjust seasoning if needed.
AccountIn:
summary: Account registration
value:
email: ada@example.com
password: FBR5h0L1ENA3edctgDIEnxSvFdR0
parameters:
'{recipeId}':
name: recipeId
in: path
required: true
schema:
$ref: '#/components/schemas/Id'
description: Unique identifier of the recipe

32
openapi/recipe-api.yaml Normal file
View File

@@ -0,0 +1,32 @@
openapi: "3.1.1"
info:
title: "Recipe API"
description:
"Programmatic access to a database of recipes.
Users can publish and find recipes."
version: "1.0.0"
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: "https://api.recipes.bitfield.eu/v1"
tags:
- name: Recipe
description: "Steps and ingredients for preparing a dish"
- name: Account
description: "Access to protected operations"
security: [ ]
paths:
/recipe/new:
$ref: "./paths/recipe-new.yaml"
/recipe/search:
$ref: "./paths/recipe-search.yaml"
/recipe/{recipeId}:
$ref: "./paths/recipe-{recipeId}.yaml"
/account/register:
$ref: "./paths/account-register.yaml"
components:
securitySchemes:
httpBasic:
type: http
scheme: basic

8
openapi/redocly.yaml Normal file
View File

@@ -0,0 +1,8 @@
extends:
- recommended
apis:
recipe-api@v1:
root: ./recipe-api.yaml
rules:
no-ambiguous-paths: error

View File

@@ -0,0 +1,9 @@
type: "object"
properties:
email:
$ref: "./Email.yaml"
password:
$ref: "./Password.yaml"
required:
- email
- password

View File

@@ -0,0 +1,2 @@
type: "string"
format: date-time

View File

@@ -0,0 +1,3 @@
type: "string"
format: email
description: "Email address"

2
openapi/schemas/Id.yaml Normal file
View File

@@ -0,0 +1,2 @@
type: "integer"
format: int64

View File

@@ -0,0 +1,2 @@
type: "string"
pattern: "^(.|\\s)*\\S(.|\\s)*$"

View File

@@ -0,0 +1,4 @@
type: "string"
format: password
minLength: 8
description: "Password"

View File

@@ -0,0 +1,24 @@
type: object
properties:
type:
type: "string"
description: "URI reference that identifies the problem type."
format: "uri"
status:
type: "integer"
description: "HTTP status code generated by the origin server for this occurrence of the problem."
format: "int32"
title:
type: "string"
description: "Short, human-readable summary of the problem type. It should not change from occurrence to occurrence of the problem, except for purposes of localization."
detail:
type: "string"
description: "Human-readable explanation specific to this occurrence of the problem."
instance:
type: "string"
description: "URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced."
properties:
type: "object"
description: "Map for additional, non-standard properties"
required:
- title

View File

@@ -0,0 +1,39 @@
type: "object"
properties:
id:
$ref: "./Id.yaml"
readOnly: true
description: "Unique identifier of the recipe"
name:
$ref: "./NonBlankString.yaml"
description: "Name"
description:
$ref: "./NonBlankString.yaml"
description: "Description"
changedAt:
$ref: "./DateTime.yaml"
readOnly: true
description: "Last point in time when the recipe was changed"
categories:
type: "array"
items:
$ref: "./NonBlankString.yaml"
description: "Category names"
ingredients:
type: "array"
minItems: 1
items:
$ref: "./NonBlankString.yaml"
description: "Step descriptions"
steps:
type: "array"
minItems: 1
items:
$ref: "./NonBlankString.yaml"
description: "Ingredient names"
required:
- name
- description
- categories
- ingredients
- steps