Skip to content

Add RecipeBlockElement and related case classes for recipe data handling#28759

Open
andresilva-guardian wants to merge 21 commits into
mainfrom
afs/recipe-block-element
Open

Add RecipeBlockElement and related case classes for recipe data handling#28759
andresilva-guardian wants to merge 21 commits into
mainfrom
afs/recipe-block-element

Conversation

@andresilva-guardian
Copy link
Copy Markdown
Contributor

@andresilva-guardian andresilva-guardian commented Apr 28, 2026

What is the value of this and can you measure success?

Recipe articles (e.g. food/2026/apr/27/pasta-bake-sumac-salad-prep-ahead-recipes-sami-tamimi) contain structured recipe data authored in Composer. This data is stored in CAPI as ElementType.RECIPE body block elements with a recipeTypeData.recipeJson blob, but we were silently discarding it, meaning DCR never received it.

We are making that structured data available to DCR, which is a prerequisite for rendering interactive recipe cards (ingredients, steps, timings, image, deep-links into the Feast app) inline in food articles.

We can measure success by verifying that RecipeBlockElement nodes are present in the FEArticle JSON payload for recipe articles (e.g. via the ?dcr=true debug view).

What does this change?

We only changed a single file: PageElement.scala.

New data model

We introduced two new case classes:

  • RecipeFeaturedImage, the recipe hero image, with required fields url, mediaId, cropId, and optional fields source, photographer, caption, imageType, width, height, mediaApiUri.
  • RecipeBlockElement, the top-level element extending PageElement, with a required id and optional title, description, featuredImage. The id is the stable recipe identifier used for Feast deep-links.

The model is intentionally minimal, exposing only the subset of fields DCR currently needs. If we need more fields, we should refer to the canonical schema at guardian/recipes-backend/schema/recipe-v3.json.

Data flow

We added a case Recipe => handler in PageElement.make(), which was previously missing (falling through to case _ => Nil). The recipeJson string from CAPI is parsed using Play JSON Reads derivation and mapped to a RecipeBlockElement.

flowchart LR
    A["CAPI\n(ElementType.RECIPE)"] --> B["recipeTypeData\n.recipeJson\n(raw JSON string)"]
    B --> C["Json.parse(json)"]
    C --> D["json.validate\n[RecipeBlockElement]"]
    D --> E["RecipeBlockElement\n(id, title, description,\nfeaturedImage)"]
    E --> F["FEArticle body blocks\n→ DCR"]
Loading

We also added RecipeBlockElement to isSupported so it passes the element filter and is included in the DCR payload.

Feast visibility

Feast visibility is determined entirely by the presence of a RecipeBlockElement. Any article with a recipe element is visible in Feast unless explicitly blocked via channels. There is no per-recipe flag for this.

Deployment order, DCR must ship first

DCR validates all incoming FEArticle payloads against an AJV JSON Schema (feArticle.json). The schema uses additionalProperties: false-style validation with explicit anyOf type discrimination, meaning any _type value not listed in the schema causes a hard validation error and returns a 500 for that article.

We must not merge this PR to main until the corresponding DCR PR (which adds RecipeBlockElement to feArticle.json and implements the recipe card renderer) has been deployed to PROD. The safe deployment order is:

flowchart TD
    A["1. Merge DCR PR\n(adds RecipeBlockElement to feArticle.json\n+ recipe card renderer)"] --> B["2. Deploy DCR PR to PROD"]
    B --> C["3. Merge this frontend PR\n(safe to merge now)"]
    C --> D["4. Deploy this frontend PR to PROD"]
Loading

Screenshots

No visual change on the frontend, this is a data pipeline change. The structured recipe data will be visible in the DCR payload (?dcr=true) for recipe articles once deployed.

Checklist


@andresilva-guardian andresilva-guardian requested a review from a team as a code owner April 28, 2026 15:44
@andresilva-guardian andresilva-guardian added the feature Departmental tracking: work on a new feature label Apr 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

@Divs-B
Copy link
Copy Markdown
Contributor

Divs-B commented May 28, 2026

The PR appears to have expected code for the Recipe element to flow to the frontend.

I have a few questions to clarify:

  1. Could you please provide a screenshot of the logs that show the RecipeBlockElement correctly flowing for any recipe? (I do not have access to the frontend logs.)
  2. Could you please confirm if we have any unit tests to ensure that we are not breaking upstream code for any mandatory data that the frontend might expect? For example, if a featured image object is present but lacks a cropId.
  3. Are you logging any failures for any recipe element if the recipe itself is malformed? (I am sure it is not, but assuming.)

implicit val ProductBlockElementWrites: Writes[ProductBlockElement] = Json.writes[ProductBlockElement]
}

case class RecipeFeaturedImage(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious to check if there is reason behind these mandatory 3 fields only for available featuredImage? Are we having same schema in CAPI?

// NOTE: This case class intentionally exposes only a subset of the full recipe schema.
// If you need to add more fields, refer to the canonical spec and extend accordingly:
// https://github.com/guardian/recipes-backend/blob/main/schema/recipe-v3.json
case class RecipeBlockElement(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that we are only seeking id and others are Optional. I trust this aligns with the requirement. However, for the sake of maintaining a minimal stack of knowledge for a recipe block element, could we also retain datePublished and author?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants