Learn-skills.dev canvas-data-fetching

Data fetching

install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/acquia/nebula/canvas-data-fetching" ~/.claude/skills/neversight-learn-skills-dev-canvas-data-fetching && rm -rf "$T"
manifest: data/skills-md/acquia/nebula/canvas-data-fetching/SKILL.md
source content

Data fetching

Data fetching with SWR

Use SWR for all data fetching. It provides caching, revalidation, and a clean hook-based API.

import useSWR from "swr";

const fetcher = (url) => fetch(url).then((res) => res.json());

export default function Profile() {
  const { data, error, isLoading } = useSWR(
    "https://my-site.com/api/user",
    fetcher,
  );

  if (error) return <div>Failed to load</div>;
  if (isLoading) return <div>Loading...</div>;
  return <div>Hello, {data.name}!</div>;
}

Fetching Drupal content with JSON:API

To fetch content from Drupal (e.g., articles, events, or other content types), use the autoconfigured

JsonApiClient
from the
drupal-canvas
package combined with
DrupalJsonApiParams
for query building.

Important: Do not mock JSON:API resources in Storybook stories. Components that fetch data will display their loading or empty states in Storybook.

import { getNodePath, JsonApiClient } from "drupal-canvas";
import { DrupalJsonApiParams } from "drupal-jsonapi-params";
import useSWR from "swr";

const Articles = () => {
  const client = new JsonApiClient();
  const { data, error, isLoading } = useSWR(
    [
      "node--article",
      {
        queryString: new DrupalJsonApiParams()
          .addSort("created", "DESC")
          .getQueryString(),
      },
    ],
    ([type, options]) => client.getCollection(type, options),
  );

  if (error) return "An error has occurred.";
  if (isLoading) return "Loading...";
  return (
    <ul>
      {data.map((article) => (
        <li key={article.id}>
          <a href={getNodePath(article)}>{article.title}</a>
        </li>
      ))}
    </ul>
  );
};

export default Articles;

Including relationships with
addInclude

When you need related entities (e.g., images, taxonomy terms), use

addInclude
to fetch them in a single request.

Avoid circular references in JSON:API responses. SWR uses deep equality checks to compare cached data, which fails with "too much recursion" errors when the response contains circular references.

Do not include self-referential fields. Fields that reference the same entity type being queried (e.g.,

field_related_articles
on an article query) create circular references: Article A references Article B, which references back to Article A. If you need related content of the same type, fetch it in a separate query.

Use

addFields
to limit the response. Always specify only the fields you need. This improves performance and helps avoid circular reference issues:

const params = new DrupalJsonApiParams();
params.addSort("created", "DESC");
params.addInclude(["field_category", "field_image"]);

// Limit fields for each entity type
params.addFields("node--article", [
  "title",
  "created",
  "field_category",
  "field_image",
]);
params.addFields("taxonomy_term--categories", ["name"]);
params.addFields("file--file", ["uri", "url"]);

Creating content list components

When building a component that displays a list of content items (e.g., a news listing, event calendar, or resource library), follow this workflow:

Setup gate

Before any JSON:API discovery or content-type checks, verify local setup:

  1. Check that a
    .env
    file exists in the project root.
  2. If
    .env
    exists, verify
    CANVAS_SITE_URL
    is set. Read
    CANVAS_JSONAPI_PREFIX
    if present; otherwise, use
    jsonapi
    .
  3. Send an HTTP request to
    {CANVAS_SITE_URL}/{CANVAS_JSONAPI_PREFIX}
    . Success means HTTP
    200
    .
  4. If the request is successful, continue with Drupal data fetching.
  5. If the request is unsuccessful (or required
    .env
    values are missing), ask the user whether they want to:
    • Configure Drupal connectivity now, or
    • Continue with static content instead of Drupal fetching.
  6. If the user chooses to configure connectivity, provide
    .env
    instructions:
    • CANVAS_SITE_URL=<their Drupal site URL>
    • CANVAS_JSONAPI_PREFIX=jsonapi
      (optional; defaults to
      jsonapi
      ) Then wait for the user to confirm they updated
      .env
      , and test the request again.
  7. If the user chooses not to configure connectivity, proceed with static content.
  8. Do not update Vite config (
    vite.config.*
    ) to troubleshoot connectivity. Connectivity issues must be resolved via correct
    .env
    values and Drupal site availability, not build tooling changes.

Step 1: Analyze the list structure

Examine the design to understand what data each list item needs:

  • What fields are displayed (title, date, image, category, etc.)?
  • How are items sorted (newest first, alphabetical, etc.)?
  • Are there filters or pagination?

Step 2: Identify or request the content type

Before writing code, verify that an appropriate content type exists in Drupal:

  1. Check the JSON:API endpoint of your local Drupal site (configured via

    CANVAS_SITE_URL
    and
    CANVAS_JSONAPI_PREFIX
    environment variables) to find a content type that matches the required structure. Use a plain
    fetch
    request for this check, after passing the Setup gate.

  2. If a matching content type exists, use it and note which fields are available.

  3. If no matching content type exists, stop and prompt the user to create one. Provide:

    • A suggested content type name
    • The required field structure based on the list design

Step 3: Build the component

Create the content list component using JSON:API to fetch content. Only use fields that actually exist on the content type—do not assume fields exist without verifying.

Handling filters

If the list includes filters based on entity reference fields (e.g., filter by category, filter by author):

  • Do not hardcode filter options. Filter options should be fetched dynamically using JSON:API.
  • Fetch the available options for each filter (e.g., all taxonomy terms in a vocabulary) and populate the filter UI from that data.

This ensures filters stay in sync with the actual content in Drupal and new options appear automatically without code changes.