--- name: canvas-data-fetching description: Fetch and render Drupal content in Canvas components with JSON:API and SWR patterns. Use when building content lists, integrating with SWR, or querying related entities. Covers JsonApiClient, DrupalJsonApiParams, relationship handling, and filter patterns. --- # Data fetching ## Data fetching with SWR Use [SWR](https://swr.vercel.app/) for all data fetching. It provides caching, revalidation, and a clean hook-based API. ```jsx 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
Failed to load
; if (isLoading) return
Loading...
; return
Hello, {data.name}!
; } ``` ## 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. ```jsx 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 ( ); }; 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: ```jsx 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=` - `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.