Skip to content

Using JSON:API Parameters

Previously, we saw that our recipe did not include all of the data for our image field, only an ID for the referenced entity.

JSON:API accepts a number of query string parameters to modify the response, for things like filtering, sorting, pagination, including related data, and restricting the fields that are returned. This makes it possible for us to include the desired image data in the response.

Let’s look at how we can add the necessary parameters to requests made by the JSON:API Client.

The queryString Option

Revisiting our call to getCollection, we can add an options object as the second argument to the method. This object can include a queryString property, which will be added to the resulting fetch request.

To include the field_media_image field, we could use the following query string: include=field_media_image

import { JsonApiClient } from "@drupal-api-client/json-api-client";
const client = new JsonApiClient(
"https://dev-drupal-api-client-poc.pantheonsite.io",
);
const recipes = await client.getCollection("node--recipe", {
queryString: "include=field_media_image",
});

If we examine the resulting recipes object, we’ll now see an included property which contains the data referenced by the field_media_image field. However, the image data is still incomplete because this media entity references file entities and we need to include that data as well. For our grid, we specifically want the thumbnail image file.

Thankfully, the include parameter allows us to follow these relationship paths with a value like: include=field_media_image.thumbnail

import { JsonApiClient } from "@drupal-api-client/json-api-client";
const client = new JsonApiClient(
"https://dev-drupal-api-client-poc.pantheonsite.io",
);
const recipes = await client.getCollection("node--recipe", {
queryString: "include=field_media_image.thumbnail",
});

Looking at the data returned, you’ll now see that the referenced thumbnail files are also part of the included array.

Generating query strings with drupal-jsonapi-params

The drupal-jsonapi-params package provides a convenient way to generate query strings for more complicated JSON:API requests. The example below demonstrates how to use this package to generate the include query string we used earlier, and also adds a filter for titles containing ‘chocolate’ to the request.

import { JsonApiClient } from "@drupal-api-client/json-api-client";
import { DrupalJsonApiParams } from "drupal-jsonapi-params";
const client = new JsonApiClient(
"https://dev-drupal-api-client-poc.pantheonsite.io",
);
const apiParams = new DrupalJsonApiParams();
const queryString = apiParams
.addInclude(["field_media_image.thumbnail"])
.addFilter("title", "chocolate", "CONTAINS")
.getQueryString();
const recipes = await client.getCollection("node--recipe", {
queryString,
});

Adding images to recipe cards

Now that we have retrieved all of the data we need, we can update our recipe grid to include images.

Umami Recipes

Deep mediterranean quiche

Difficulty: medium

A delicious deep layered Mediterranean quiche with basil garnish View Recipe

Vegan chocolate and nut brownies

Difficulty: medium

A stack of chocolate and pecan brownies, sprinkled with pecan crumbs and crushed walnut, fresh out of the oven View Recipe

Super easy vegetarian pasta bake

Difficulty: easy

Cheesy pasta dish with vegetarian sausages and topped with mozzarella cheese and basil View Recipe

Watercress soup

Difficulty: easy

Watercress soup with a sprig of coriander as garnish in a white bowl with green trim View Recipe

Victoria sponge cake

Difficulty: easy

A classic, uncut Victoria sponge with a deep filling of butter cream and jam View Recipe

Gluten free pizza

Difficulty: medium

Olives, basil, and mozzarella top a gluten free pizza crust with marinara sauce View Recipe

Thai green curry

Difficulty: medium

A traditional bowl of creamy, aromatic Thai green curry with chunks of chicken in a small bowl with jasmine rice View Recipe

Crema catalana

Difficulty: medium

Typical Catalan dessert made from cream and egg yolks, covered with a traditional layer of caramelized sugar to provide a crispy contrast View Recipe

Fiery chili sauce

Difficulty: easy

An iridescent array of chilies, onions, and garlic, slowly sweating over a low heat View Recipe

Borscht with pork ribs

Difficulty: medium

Traditional Ukrainian soup with beets, tomatoes, and pork ribs View Recipe
---
import { JsonApiClient } from "@drupal-api-client/json-api-client";
const client = new JsonApiClient(
"https://dev-drupal-api-client-poc.pantheonsite.io",
);
const recipes = await client.getCollection("node--recipe", {
queryString: "include=field_media_image.thumbnail",
});
---
<style>
.card-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1rem;
}
.card-grid .card {
display: flex;
flex-direction: column;
justify-content: space-between;
border: 2px solid var(--sl-color-text-accent);
margin-top: 0;
padding: 0.5rem;
}
</style>
<div>
<h2>Umami Recipes</h2>
<div class="card-grid">
{recipes.data.map((recipe) => {
// Get field_media_image referenced on the recipe
const mediaId = recipe.relationships.field_media_image.data.id;
const mediaEntity = recipes.included.find((obj) => obj.id === mediaId);
// Get the thumbnail file entity referenced on field_media_image
const fileId = mediaEntity.relationships.thumbnail.data.id;
const alt = mediaEntity.relationships.thumbnail.data.meta.alt;
const fileEntity = recipes.included.find((obj) => obj.id === fileId);
return (
<div class="card" key={recipe.id}>
<h4>{recipe.attributes.title}</h4>
<div>
<p>Difficulty: {recipe.attributes.field_difficulty}</p>
<img
src=`${client.baseUrl}${fileEntity.attributes.uri.url}`
alt={alt}
/>
<a href={recipe.attributes.path.alias}>View Recipe</a>
</div>
</div>
);
})}
</div>
</div>

You may have noticed that traversing entity relationships to determine the image path in the above example required a number of steps.

// Get field_media_image referenced on the recipe
const mediaId = recipe.relationships.field_media_image.data.id;
const mediaEntity = recipes.included.find((obj) => obj.id === mediaId);
// Get the thumbnail file entity referenced on field_media_image
const fileId = mediaEntity.relationships.thumbnail.data.id;
const alt = mediaEntity.relationships.thumbnail.data.meta.alt;
const fileEntity = recipes.included.find((obj) => obj.id === fileId);

The JSON:API Client can help with this as well. Next, we’ll adapt this example to deserialize the response in order to simplify the process of working with JSON:API relationships.

Additional Resources