canvas-data-fetching

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Data fetching

数据获取

Data fetching with SWR

使用SWR进行数据获取

Use SWR 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 <div>Failed to load</div>;
  if (isLoading) return <div>Loading...</div>;
  return <div>Hello, {data.name}!</div>;
}
所有数据获取操作均使用SWR完成。它提供缓存、重新验证功能,以及简洁的基于Hook的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 <div>Failed to load</div>;
  if (isLoading) return <div>Loading...</div>;
  return <div>Hello, {data.name}!</div>;
}

Fetching Drupal content with JSON:API

使用JSON:API获取Drupal内容

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 fabricate JSON:API resource payloads in Workbench mocks. Components that fetch data should render their real loading, empty, or error states in Workbench unless the user explicitly asks for a static, non-fetching preview shape.
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 (
    <ul>
      {data.map((article) => (
        <li key={article.id}>
          <a href={getNodePath(article)}>{article.title}</a>
        </li>
      ))}
    </ul>
  );
};

export default Articles;
要从Drupal中获取内容(例如文章、活动或其他内容类型),请结合使用
drupal-canvas
包中自动配置的
JsonApiClient
和用于构建查询的
DrupalJsonApiParams
重要提示: 不要在Workbench模拟中伪造JSON:API资源负载。除非用户明确要求静态、非获取数据的预览形态,否则获取数据的组件应在Workbench中渲染其真实的加载、空数据或错误状态。
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 (
    <ul>
      {data.map((article) => (
        <li key={article.id}>
          <a href={getNodePath(article)}>{article.title}</a>
        </li>
      ))}
    </ul>
  );
};

export default Articles;

Including relationships with
addInclude

使用
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']);
当你需要获取关联实体(例如图片、分类术语)时,请使用
addInclude
在单次请求中获取它们。
避免JSON:API响应中出现循环引用。 SWR使用深度相等检查来比较缓存数据,当响应包含循环引用时会触发“递归过深”错误。
不要包含自引用字段。 引用正在查询的同一实体类型的字段(例如文章查询中的
field_related_articles
)会创建循环引用:文章A引用文章B,而文章B又引用回文章A。如果你需要同一类型的关联内容,请通过单独的查询获取。
使用
addFields
限制响应内容。
始终只指定你需要的字段。这可以提升性能,并有助于避免循环引用问题:
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=<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.
在进行任何JSON:API发现或内容类型检查之前,请先验证本地环境配置:
  1. 检查项目根目录下是否存在
    .env
    文件。
  2. 如果
    .env
    文件存在,验证是否已设置
    CANVAS_SITE_URL
    。如果存在
    CANVAS_JSONAPI_PREFIX
    则读取该值;否则使用默认值
    jsonapi
  3. {CANVAS_SITE_URL}/{CANVAS_JSONAPI_PREFIX}
    发送HTTP请求。请求成功的标志是返回HTTP
    200
    状态码。
  4. 如果请求成功,继续进行Drupal数据获取操作。
  5. 如果请求失败(或缺少必要的
    .env
    配置值),询问用户是否希望:
    • 立即配置Drupal连接,或者
    • 继续使用静态内容而非从Drupal获取数据。
  6. 如果用户选择配置连接,请提供
    .env
    配置说明:
    • CANVAS_SITE_URL=<你的Drupal站点URL>
    • CANVAS_JSONAPI_PREFIX=jsonapi
      (可选;默认值为
      jsonapi
      )。然后等待用户确认已更新
      .env
      文件,并重试请求。
  7. 如果用户选择不配置连接,则继续使用静态内容。
  8. 不要修改Vite配置文件(
    vite.config.*
    )来排查连接问题。连接问题必须通过正确的
    .env
    配置值和Drupal站点的可用性来解决,而非修改构建工具配置。

Step 1: Analyze the list structure

步骤1:分析列表结构

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

步骤2:识别或请求创建内容类型

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
在编写代码之前,请验证Drupal中是否存在合适的内容类型:
  1. 通过
    CANVAS_SITE_URL
    CANVAS_JSONAPI_PREFIX
    环境变量配置的本地Drupal站点的JSON:API端点,查找与所需结构匹配的内容类型。通过前置配置检查后,使用普通的
    fetch
    请求完成此检查。
  2. 如果存在匹配的内容类型,请使用它并记录可用的字段。
  3. 如果不存在匹配的内容类型,请停止操作并提示用户创建一个。提供:
    • 建议的内容类型名称
    • 基于列表设计所需的字段结构

Step 3: Build the component

步骤3:构建组件

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.
使用JSON:API获取内容,创建内容列表组件。仅使用内容类型上实际存在的字段——不要在未验证的情况下假设字段存在。

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.
如果列表包含基于实体引用字段的过滤器(例如按分类筛选、按作者筛选):
  • 不要硬编码过滤器选项。 过滤器选项应通过JSON:API动态获取。
  • 获取每个过滤器的可用选项(例如词汇表中的所有分类术语),并据此填充过滤器UI。
这样可以确保过滤器与Drupal中的实际内容保持同步,新选项无需修改代码即可自动显示。