Skip to content

Deserializing Data

When adding images to our recipes grid, we had to do quite a bit of traversing to get the data we needed due to the structure of relationships and included data in JSON:API responses.

// 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);

We can simplify this by using the serializer option when creating our client instance, along with a library like Jsona. This will provide a simplified and flattened response when data is returned from the server.

Revisiting the creation of our JsonApiClient instance, we can add an options object as the second argument. This object can include a serializer property, which can be used to transform the data provided by JSON:API.The provided serializer object can have two methods: deserializer and serializer. An instance of Jsona fits that signature. The package also provides DefaultSerializer, which extends Jsona and attaches JSON:API top-level meta and links to deserialized results via getMeta() and getLinks() (see DefaultSerializer: Top-level meta and links below).

Let’s update our client instance to use the Jsona serializer.

import { JsonApiClient } from "@drupal-api-client/json-api-client";
import { Jsona } from "jsona";
const client = new JsonApiClient("https://drupal-api-demo.party", {
serializer: new Jsona(),
});
Section titled “DefaultSerializer: Top-level meta and links”

JSON:API documents can include top-level meta and links (for example, pagination or copyright). If you need these without mixing them into the resource object, use DefaultSerializer from @drupal-api-client/json-api-client (or @drupal-api-client/utils). It extends Jsona and exposes top-level meta and links via getMeta() and getLinks() on the deserialized result.

import {
JsonApiClient,
DefaultSerializer,
} from "@drupal-api-client/json-api-client";
const client = new JsonApiClient("https://drupal-api-demo.party", {
serializer: new DefaultSerializer(),
});
// Single resource: getMeta() and getLinks() on the resource
const recipe = await client.getResource(
"node--recipe",
"35f7cd32-2c54-49f2-8740-0b0ec2ba61f6",
);
recipe.getMeta(); // top-level "meta" or undefined
recipe.getLinks(); // top-level "links" or undefined
// Collection: getMeta() and getLinks() on the array
const recipes = await client.getCollection("node--recipe");
recipes.getMeta(); // e.g. { count: 10 }
recipes.getLinks(); // e.g. { self: "...", next: "..." }

The deserialized shape is the same as with Jsona (flattened attributes, etc.). DefaultSerializer also provides serialize(), so you can re-serialize data when creating or updating resources. Meta and links are stored as non-enumerable properties, so they do not appear in Object.keys() or when serializing the result.

Now that we have provided a compatible serializer, all data sourced from Drupal will be deserialized automatically.

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

Taking a look at the recipes object, you’ll see that the data is now flatter and easier to access. For example, field_media_image is now a direct property of the recipe data, and the thumbnail file is directly accessible from this media entity. We could get the thumbnail url for the first recipe result like this:

const thumbnailUrl = recipe[0].field_media_image.thumbnail.uri.url;

Let’s see the impact that working with deserialized data has on our recipe grid. We can refactor the grid to use this new flattened data structure.

---
import { JsonApiClient } from "@drupal-api-client/json-api-client";
import { Jsona } from "jsona";
const client = new JsonApiClient(
"https://drupal-api-demo.party", {
serializer: new Jsona(),
},
);
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.map((recipe) => (
<div class="card" key={recipe.id}>
<h4>{recipe.title}</h4>
<div>
<p>Difficulty: {recipe.field_difficulty}</p>
<img
src=`${client.baseUrl}${recipe.field_media_image.thumbnail.uri.url}`
alt={recipe.field_media_image.thumbnail.resourceIdObjMeta.alt}
/>
<a href={recipe.path.alias}>View Recipe</a>
</div>
</div>
))}
</div>
</div>

The end result is the same, so choose the option that is best for your team. Use the client default to work with the JSON:API response as is, or use the serializer option to work with a simplified object.

In cases where you need serialize or re-serialize the data before sending it to the server, you will have access to the serializer method on the client instance. Below is an example of using Jsona’s serializer.

import { JsonApiClient } from "@drupal-api-client/json-api-client";
import { Jsona } from "jsona";
const client = new JsonApiClient("https://drupal-api-demo.party", {
serializer: new Jsona(),
});
// This recipe will be deserialized automatically
const recipe = await getResource(
"node--recipe",
"35f7cd32-2c54-49f2-8740-0b0ec2ba61f6",
);
// If we later need to re-serialize the recipe we can do so like this
const serializedRecipe = client.serializer.serialize({ stuff: recipe });

Since the signature of these serializer methods tend to vary, the JSON:API client doesn’t currently serialize data automatically, but it will be available for use if provided.

  • Jsona
  • jsonapi-serializer
  • jsonapi-fractal
  • DefaultSerializer — provided by @drupal-api-client/utils and re-exported by @drupal-api-client/json-api-client for access to top-level meta and links via getMeta() and getLinks().