Canva Apps SDK実践ガイド


# はじめに – Canva Apps SDKの概要と魅力

Canvaは、世界で1億3,500万人以上が利用する人気のオンラインデザインプラットフォームです。[^1] 豊富なテンプレートと直感的な操作性で、プロから初心者まで幅広い層に支持されています。Canva Apps SDKは、このCanvaプラットフォームを拡張し、ユーザーに新たな価値を提供できる強力なツールです。

### Canva Apps SDKで広がる可能性

Canva Apps SDKを使うことで、Canvaのエディタ内に独自の機能やコンテンツを組み込んだアプリケーションを開発できます。CanvaのAPIを活用することで、デザインプロセスを自動化したり、外部サービスと連携したり、Canvaの機能を拡張する様々なアプリを作成できます。

### Canva Apps SDKを使うメリット

*   **Canvaの広大なユーザーベースにリーチ:** 1億3,500万人以上のCanvaユーザーに、自作のアプリを公開し、利用してもらう機会が得られます。[^1]
*   **デザインワークフローの効率化:**  Canvaの機能を拡張することで、デザインプロセスを自動化し、Canvaユーザーの業務効率を劇的に向上させることができます。
*   **新たなビジネスチャンスの創出:**  Canva Apps Marketplaceでアプリを公開・販売することで、新たな収益源を確立できます。
*   **Canvaプラットフォームへの貢献:**  Canvaのエコシステムを豊かにし、Canvaユーザーに新たな価値を提供することで、Canvaプラットフォームへの貢献と、開発者としてのスキルアップを両立できます。

### 本書で学べること

本書は、Canva Apps SDKを用いたアプリ開発の入門書として、開発環境の構築から実際のコード実装、そしてCanva Apps Marketplaceへの公開まで、一連の流れを丁寧に解説します。

具体的には、

*   **開発環境構築:**  Canva Apps SDKを利用するための開発環境を構築手順
*   **アプリの基本:**  簡単な「Hello World」アプリの作成を通して、アプリ開発の基本を習得
*   **APIリファレンス:**  Apps SDKが提供するAPIの使い方、Canvaエディタとの連携方法
*   **実践的な機能:**  デザイン要素の操作、ドラッグ&ドロップ機能、外部データ連携など、応用的な機能の実装
*   **公開準備:**  アプリの審査申請から公開までの手順、ストア掲載に向けた準備

本書を通じて、Canva Apps SDKの基本的な使い方をマスターし、アイデアを形にするための第一歩を踏み出しましょう。Canvaアプリ開発は、あなたの創造性をCanvaユーザーに届け、新たなデザイン体験を提供する絶好の機会となるはずです。

## 開発準備 – 開発者登録と環境構築

Canva Apps SDKを利用したアプリ開発を始めるには、まずCanvaの開発者登録を行い、開発環境を構築する必要があります。この章では、その手順をステップごとに解説します。

### 1. Canva開発者アカウント登録

Canvaアプリ開発には、Canvaアカウントが必須です。Canvaの公式サイトからアカウントを登録してください。登録は無料プランでも可能です。

[Canva公式サイト](https://www.canva.com/)

アカウント登録後、Canvaの開発者ポータルにアクセスし、開発者登録を行います。

[Canva Developer Portal](https://www.canva.com/developers)

開発者ポータルでは、アプリの作成、設定、プレビュー、申請、公開など、アプリ開発に必要な様々な操作を行います。

### 2. 開発環境の構築

Canva Apps SDKを使ったアプリ開発には、以下の環境が必要です。

*   **Node.js**:  JavaScript実行環境。v20.10.0 LTSバージョンを推奨
*   **npm (Node Package Manager)**:  Node.jsのパッケージ管理ツール。v10以上を推奨
*   **Canva CLI (Command Line Interface)**:  Canvaアプリ開発用のコマンドラインツール
*   **コードエディタ**:  Visual Studio Code (VSCode) など、お好みのエディタ
*   **Webブラウザ**:  Google Chrome推奨

#### Node.js / npm のインストール

Node.js公式サイトからインストーラーをダウンロードし、インストールします。npmはNode.jsに同梱されています。

[Node.js公式サイト](https://nodejs.org/)

インストール後、ターミナルで以下のコマンドを実行し、バージョンを確認します。

```bash
node -v
npm -v
```

#### Canva CLI のインストール

Canva CLIはnpmを使ってインストールします。ターミナルで以下のコマンドを実行します。

```bash
npm install -g @canva/cli@latest
```

インストール後、`canva -v`コマンドでバージョンを確認します。

#### Canva CLI へのログイン

Canva CLIを利用するには、Canvaアカウントでのログインが必要です。ターミナルで`canva login`コマンドを実行します。

```bash
canva login
```

ブラウザが自動で開き、Canvaの開発者ポータルへのアクセス許可を求められます。「Allow」をクリックして許可し、表示されたコードをターミナルにコピー&ペーストしてEnterキーを押します。

#### 開発用テンプレートの利用

Canva CLIでは、アプリ開発を始めるためのテンプレートが用意されています。テンプレートを利用することで、開発環境の初期設定を大幅に簡略化できます。

テンプレートを利用しない場合は、starter kitをダウンロードして環境構築を行うことも可能です。詳細は、Canva Developersドキュメントの[Setting up the starter kit](https://www.canva.dev/docs/apps/setting-up-starter-kit/)を参照してください。

### 3. プロジェクトの作成

Canva CLIを使って、Hello Worldテンプレートをベースにしたプロジェクトを作成します。ターミナルで以下のコマンドを実行します。`<your-app-name>`は任意のアプリ名に置き換えてください。

```bash
canva apps create --template hello_world "<your-app-name>"
```

例:

```bash
canva apps create --template hello_world "my-hello-world-app"
```

コマンド実行後、いくつかの質問に答える形式で設定を行います。

*   **Audience**: アプリの公開範囲を選択します。Public (一般公開) か Team (チーム内利用) を選択します。ここでは Public を選択します。
*   **App name**: アプリ名を入力します。
*   **Git**: Gitリポジトリを初期化するかどうかを選択します。Yes を選択します。
*   **Dependencies**: 依存関係をインストールするかどうかを選択します。Yes を選択します。

設定が完了すると、プロジェクトの作成が開始され、必要なファイルが自動生成されます。

### 4. 開発サーバーの起動とプレビュー

プロジェクト作成が完了したら、開発サーバーを起動し、Canvaエディタでアプリをプレビューしてみましょう。

1.  プロジェクトのディレクトリに移動します。

```bash
cd <your-app-name>
```

例:

```bash
cd my-hello-world-app
```

2.  開発サーバーを起動します。

```bash
npm start
```

3.  Canvaの開発者ポータルで、作成したアプリの設定ページを開き、「プレビュー」ボタンをクリックします。

4.  Canvaエディタが起動し、サイドパネルにアプリがプレビュー表示されます。
5.  サイドパネルに表示された「Do something cool」ボタンをクリックすると、デザインキャンバスに「Hello world!」と書かれたテキスト要素が追加されることを確認します。

これで、Canva Apps SDKの開発環境構築と、Hello Worldアプリのプレビュー表示が完了しました。次章では、生成されたプロジェクトの構成とSDKの基礎について解説します。

## 初めてのアプリ作成 – Hello Worldを表示しよう

前章で構築した開発環境を使って、Canvaエディタ上に「Hello World」と表示するシンプルなアプリを作成し、Canvaアプリ開発の基本的な流れを体験しましょう。

### 1. プロジェクトの作成

前章の手順に従い、Canva CLIでHello Worldテンプレートのプロジェクトを作成します。

```bash
canva apps create --template hello_world "my-hello-world-app"
```

### 2. コードの確認 (src/app.tsx)

プロジェクト作成後、`src/app.tsx`ファイルを開き、コードを確認します。

```tsx
import React from 'react';
import { Button, Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
import { useAddElement } from 'utils/use_add_element';

export const App = () => {
  const addElement = useAddElement();

  const onClick = () => {
    addElement({
      type: 'text',
      children: ['Hello world!'],
    });
  };

  const intl = useIntl();

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="
              To make changes to this app, edit the <code>src/app.tsx</code> file,
              then close and reopen the app in the editor to preview the changes.
            "
            description="
              Instructions for how to make changes to the app. Do not translate
              <code>src/app.tsx</code>.
            "
            values={{
              code: (chunks) => <code>{chunks}</code>,
            }}
          />
        </Text>
        <Button variant="primary" onClick={onClick} stretch>
          {intl.formatMessage({
            defaultMessage: 'Do something cool',
            description:
              'Button text to do something cool. Creates a new text element when pressed.',
          })}
        </Button>
      </Rows>
    </div>
  );
};
```

このコードは、Canva Apps SDKの基本的な構成要素であるReactコンポーネントとAPIメソッドの使い方を示しています。

*   **import**:  必要なモジュールをインポート (`react`, `@canva/app-ui-kit`, `@canva/design`, `react-intl`)
*   **Appコンポーネント**:  アプリのメインコンポーネント (`export const App = () => { ... }`)
*   **UI Kitコンポーネント**:  Canvaのデザインシステムに沿ったUIコンポーネント (`Button`, `Rows`, `Text`)
*   **FormattedMessage**:  テキストの国際化対応コンポーネント
*   **useIntl**:  国際化対応のためのフック
*   **useAddElement**:  Canvaのデザインに要素を追加するカスタムフック
*   **onClick**:  ボタンクリック時の処理 (テキスト要素を追加)
*   **render**:  UIをJSXで記述

### 3. アプリのプレビュー

Canva Developer Portalからアプリをプレビュー表示し、動作を確認します。

1.  Canva Developer Portalで、作成したアプリの設定ページを開きます。
2.  「プレビュー」ボタンをクリックします。
3.  Canvaエディタが起動し、サイドパネルにアプリがプレビュー表示されます。
4.  サイドパネルに表示された「Do something cool」ボタンをクリックします。
5.  Canvaエディタのデザインキャンバスに「Hello world!」と書かれたテキスト要素が追加されることを確認します。


### 4. コードの編集 (src/app.tsx)

`src/app.tsx`ファイルを編集し、ボタンの表示テキストを変更してみましょう。

```tsx
<Button variant="primary" onClick={onClick} stretch>
  {intl.formatMessage({
    defaultMessage: 'Click me!', // Changed text
    description:
      'Button text to do something cool. Creates a new text element when pressed.',
  })}
</Button>
```

`defaultMessage`の`'Do something cool'`を`'Click me!'`に変更し、保存します。

Canvaエディタのプレビュー画面に戻ると、ボタンの表示が「Click me!」に変わっていることを確認できます。

このように、Canva Apps SDKでは、コードを編集するとすぐにプレビュー画面に反映されるため、効率的に開発を進めることができます。

### 5. Gitリポジトリへの登録

`canva apps create`コマンド実行時にGitリポジトリを初期化するオプションを選択した場合、プロジェクトは既にGitリポジトリとして管理されています。

```bash
git status
```

コマンドを実行し、変更がcommitされていないことを確認します。

```bash
git add .
git commit -m "feat: ボタン表示テキストをClick me!に変更"
```

上記コマンドを実行し、変更をGitリポジトリにcommitします。

## Canvaアプリの仕組み – プロジェクト構成とSDK基礎

Hello Worldアプリの作成を通して、Canvaアプリ開発の一連の流れを体験しました。この章では、Canvaアプリのプロジェクト構成と、Canva Apps SDKの基礎について解説します。

### 1. プロジェクト構成

Canva CLIで生成されたプロジェクトは、以下のファイルとフォルダで構成されています。

*   `dist/`:  ビルドされたアプリの出力先フォルダ (`app.js` など)
*   `public/`:  静的ファイル (画像、JSONデータなど) を配置するフォルダ
*   `src/`:  アプリのソースコードを配置するフォルダ
    *   `app.tsx`:  アプリのエントリーポイントとなるReactコンポーネント
    *   `app.messages.ts`:  アプリのUIテキストを定義するファイル (react-intl用)
    *   `index.tsx`:  Reactアプリの初期化とレンダリングを行うファイル
    *   `styles/`:  CSSモジュールを配置するフォルダ (`components.css` など)
    *   `utils/`:  ユーティリティ関数やカスタムフックを配置するフォルダ (`useAddElement.ts`, `useOverlayHook.ts`, `table_wrapper.ts` など)
*   `types/`:  TypeScriptの型定義ファイルを配置するフォルダ
*   `webpack.config.js`:  webpackの設定ファイル
*   `jest.config.js`:  Jestの設定ファイル
*   `.eslintrc.cjs`:  ESLintの設定ファイル
*   `.nvmrc`:  Node.jsのバージョン指定ファイル
*   `.prettierrc.js`:  Prettierの設定ファイル
*   `package.json`:  npmの設定ファイル (依存関係、スクリプトなど)
*   `tsconfig.json`:  TypeScriptの設定ファイル

これらのファイルとフォルダを編集することで、Canvaアプリの機能やUIを実装していきます。

### 2. Manifest (manifest.json)

`manifest.json`ファイルは、Canva Apps SDKで開発されたアプリの設定ファイルです。アプリ名、説明、アイコン、利用可能な機能 (API permissions)、Canva上での表示場所 (UI surfaces) などを定義します。

```json
{
  "name": "My Hello World App",
  "description": "My first Canva App!",
  "icon": "./logo.svg",
  "uiSurfaces": [
    {
      "type": "OBJECT_PANEL"
    }
  ],
  "permissions": []
}
```

*   **name**:  アプリ名 (Apps MarketplaceやCanvaエディタに表示)
*   **description**:  アプリの説明 (Apps Marketplaceなどに表示)
*   **icon**:  アプリのアイコン画像へのパス (publicフォルダからの相対パス)
*   **uiSurfaces**:  アプリをCanvaのどのUI surfaceで利用可能にするかを定義
    *   `OBJECT_PANEL`:  オブジェクトパネル (Canvaエディタのサイドパネル)
    *   `SELECTED_IMAGE_OVERLAY`:  選択された画像の上にオーバーレイ表示
    *   `HEADLESS`:  UIを持たないバックグラウンド処理用 (API経由でのみ利用)
*   **permissions**:  アプリが利用するAPI permission (Canvaの機能へのアクセス権限) を定義

`manifest.json`を編集することで、アプリの基本情報やCanva上での挙動をカスタマイズできます。

### 3. SDK基礎 – Reactコンポーネント

Canva Apps SDKは、UI構築にReactコンポーネントをApp UI Kitのコンポーネントを使用することが推奨されています。App UI Kitは、Canvaのデザインシステムに沿ったUIコンポーネントライブラリであり、Canvaのデザインガイドラインに準拠したアプリUIを効率的に作成できます。

Hello Worldアプリでも、`@canva/app-ui-kit`から`Button`, `Rows`, `Text`コンポーネントをインポートし、UIを構築しています。

```tsx
import { Button, Rows, Text } from '@canva/app-ui-kit';
```

これらのコンポーネントを組み合わせることで、Canvaのデザインに調和したUIを実装できます。App UI Kitには、ボタン、テキスト、アイコン、レイアウトコンポーネントなど、 다양한UIコンポーネントが用意されています。

### 4. SDK基礎 – APIメソッド

Canva Apps SDKは、Canvaエディタと連携するための様々なAPIメソッドを提供しています。APIメソッドを使うことで、Canvaのデザイン要素の操作、ユーザーデータの取得、アセットのアップロードなど、Canvaの機能を拡張する様々な処理を実装できます。

Hello Worldアプリでは、`utils/useAddElement.ts`で定義されたカスタムフック `useAddElement` を通して、`@canva/design` パッケージの `addElementAtPoint` APIメソッドを間接的に利用しています。

```tsx
import { addElementAtPoint } from '@canva/design';

export const useAddElement = () => {
  const addElement = React.useCallback(
    async ({ type, children }: { type: string; children: string[] }) => {
      await addElementAtPoint({
        element: {
          type,
          x: 0,
          y: 0,
          width: 500,
          height: 100,
          props: {
            children,
          },
        },
      });
    },
    [],
  );

  return addElement;
};
```

`addElementAtPoint` APIメソッドは、Canvaのデザイン上に要素を追加する機能を提供します。Hello Worldアプリでは、ボタンクリック時にテキスト要素をデザインの左上 (0, 0) の位置に追加する処理を実装しています。

Apps SDKには、他にも様々なAPIメソッドが用意されており、アプリの機能に合わせてAPIを組み合わせて利用します。APIリファレンスドキュメントを参照し、利用可能なAPIとその使い方を理解しましょう。

### 5. SDK基礎 – 権限 (Permissions)

Canva Apps SDKでは、APIメソッドごとに必要な権限 (permissions) が定義されています。権限は、アプリがCanvaのどの機能にアクセスできるかを制御するための仕組みです。

例えば、デザイン要素を操作するAPIメソッドを利用するには、`canva:design:content:write` 権限が必要です。権限設定は、Developer Portalのアプリ設定ページで行います。

Hello Worldアプリでは、Canvaの情報を読み書きするAPIを利用していないため、特に権限設定は必要ありません。しかし、より高度な機能を持つアプリを開発する際には、適切な権限設定が必要になります。

## デザイン要素の操作 – ボタンでオブジェクト追加

Canva Apps SDKのAPIメソッドを使うことで、Canvaのデザイン上に様々な要素 (オブジェクト) を追加したり、編集したりできます。この章では、ボタンをクリックすることで、デザイン上にテキスト要素と図形要素を自由に追加できるアプリを作成します。

### 1. UIの作成 (src/app.tsx)

`src/app.tsx`を編集し、UIを実装します。App UI Kitの`Button`コンポーネントを2つ配置し、それぞれ「テキストを追加」「図形を追加」というラベルを表示します。

```tsx
import React from 'react';
import { Button, Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
import { useAddElement } from 'utils/use_add_element';

export const App = () => {
  const addElement = useAddElement();
  const intl = useIntl();

  const handleAddText = () => { /* テキスト追加処理を実装 (後述) };
  const handleAddShape = () => { // 図形追加処理を実装 (後述) };

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="Canva API Example App"
            description="App name"
          />
        </Text>
        <Button variant="primary" onClick={handleAddText} stretch>
          {intl.formatMessage({
            defaultMessage: 'テキストを追加', // テキストを追加
            description: 'テキストを追加',
          })}
        </Button>
        <Button variant="primary" onClick={handleAddShape} stretch>
          {intl.formatMessage({
            defaultMessage: '図形を追加', // 図形を追加
            description: '図形を追加',
          })}
        </Button>
      </Rows>
    </div>
  );
};
```

### 2. テキスト要素追加処理の実装 (handleAddText)

`handleAddText`関数を実装し、ボタンクリック時にテキスト要素をCanvaデザインに追加する処理を記述します。`@canva/design` パッケージから `addElementAtPoint` APIをインポートし、`useAddElement`カスタムフックは削除します。

```tsx
import React from 'react';
import { Button, Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
// import { useAddElement } from 'utils/use_add_element'; // useAddElementカスタムフックを削除
import { addElementAtPoint } from '@canva/design'; // addElementAtPoint APIをインポート

export const App = () => {
  // const addElement = useAddElement(); // useAddElementカスタムフックを削除
  const intl = useIntl();

  const handleAddText = async () => { // 非同期関数に変更
    await addElementAtPoint({ // APIメソッドを直接呼び出し
      type: 'text',
      element: {
        type: 'text',
        x: 0,
        y: 0,
        width: 500,
        height: 100,
        props: {
          children: ['Hello world! (テキスト要素)'], // テキスト内容を修正
        },
      },
    });
  };
  const handleAddShape = () => { // 図形追加処理は後述 };

  // ... (以下省略)
```

`addElementAtPoint` API メソッドの `element` プロパティに、追加する要素の情報をオブジェクト形式で指定します。

*   `type: 'text'`:  要素のタイプ (テキスト要素) を指定
*   `x: 0, y: 0`:  要素の配置位置 (X座標, Y座標) を指定
*   `width: 500, height: 100`:  要素のサイズ (幅, 高さ) を指定
*   `props: { children: ['Hello world! (テキスト要素)'] }`:  テキスト要素の内容を指定

### 3. 図形要素追加処理の実装 (handleAddShape)

`handleAddShape`関数を実装し、ボタンクリック時に図形要素 (四角形) をCanvaデザインに追加する処理を記述します。

```tsx
const handleAddShape = async () => { // 非同期関数に変更
  await addElementAtPoint({ // APIメソッドを直接呼び出し
    element: {
      type: 'shape',
      x: 600,
      y: 0,
      width: 200,
      height: 200,
      props: {
        backgroundColor: '#ff0000', // 背景色を赤色に指定
        type: 'rect', // 図形タイプを四角形に指定
      },
    },
  });
};
```

`addElementAtPoint` API メソッドの `element` プロパティに、追加する図形要素の情報をオブジェクト形式で指定します。

*   `type: 'shape'`:  要素のタイプ (図形要素) を指定
*   `x: 600, y: 0`:  要素の配置位置 (X座標, Y座標) を指定 (テキスト要素と位置が重ならないようにX座標を調整)
*   `width: 200, height: 200`:  要素のサイズ (幅, 高さ) を指定
*   `props: { backgroundColor: '#ff0000', type: 'rect' }`:  図形要素のプロパティを指定
    *   `backgroundColor: '#ff0000'`:  図形の色を赤色に指定
    *   `type: 'rect'`:  図形タイプを四角形に指定

### 4. 動作確認

Canvaエディタのプレビュー画面で、ボタンをクリックし、テキスト要素と図形要素がデザインに追加されることを確認します。

*   「テキストを追加」ボタンをクリックすると、「Hello world! (テキスト要素)」というテキスト要素がデザインの左上に追加されます。
*   「図形を追加」ボタンをクリックすると、赤い四角形の図形要素がデザインに追加されます。

## ドラッグ＆ドロップ実装 – アプリからキャンバスへ素材配置

Canvaの魅力の一つであるドラッグ&ドロップ操作をアプリに実装することで、より直感的でCanvaらしいユーザー体験を提供できます。この章では、App UI KitのコンポーネントとApps SDKのAPIを組み合わせて、アプリからキャンバスへのドラッグ&ドロップ機能 を実装する方法を解説します。

### 1. UIコンポーネントの準備 (src/app.tsx)

`src/app.tsx`を編集し、ドラッグ&ドロップ可能なUIコンポーネントを準備します。ここでは、App UI Kitの`ImageCard`コンポーネントを使用します。

```tsx
import React from 'react';
import { ImageCard, Rows, Text } from '@canva/app-ui-kit'; // ImageCardコンポーネントを追加
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
import { addElementAtPoint, ui } from '@canva/design'; // uiオブジェクトを追加

export const App = () => {
  // ... (省略)

  const handleAddText = () => { /* 省略 */ };
  const handleAddShape = () => { /* 省略 */ };
  const handleDragStart = (event) => { /* ドラッグ開始時の処理 (後述) */ };
  const handleClick = () => { /* カードクリック時の処理 (後述) */ };

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="Canva API Example App"
            description="App name"
          />
        </Text>
        <Button variant="primary" onClick={handleAddText} stretch>
          {intl.formatMessage({
            defaultMessage: 'テキストを追加',
            description: 'テキストを追加',
          })}
        </Button>
        <Button variant="primary" onClick={handleAddShape} stretch>
          {intl.formatMessage({
            defaultMessage: '図形を追加',
            description: '図形を追加',
          })}
        </Button>
        <Rows spacing="1u">
          {/* ImageCardコンポーネントを追加 */}
          <ImageCard
            ariaLabel="Add image to design"
            alt="Example image"
            thumbnailUrl="https://www.canva.dev/example-assets/image-import/image-card-thumbnail.png" // サムネイルURL
            onClick={handleClick} // クリック時の処理 (後述)
            draggable // ドラッグ可能にする
            onDragStart={handleDragStart} // ドラッグ開始時の処理 (後述)
          />
        </Rows>
      </Rows>
    </div>
  );
};
```

`ImageCard`コンポーネントに`draggable`属性と`onDragStart`属性を追加し、ドラッグ&ドロップ機能を有効にします。`thumbnailUrl`プロパティには、ドラッグ&ドロップ時に表示するサムネイル画像のURLを指定します。

### 2. ドラッグ開始時の処理を実装 (handleDragStart)

`handleDragStart`関数を実装し、ドラッグ開始時にCanvaに要素を渡す処理を記述します。`@canva/design` パッケージから `ui` オブジェクトをインポートし、`ui.startDragToPoint` APIメソッドを呼び出します。

```tsx
import { ImageCard, Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
import { addElementAtPoint, ui } from '@canva/design'; // uiオブジェクトを追加

export const App = () => {
  // ... (省略)

  const handleAddText = () => { /* 省略 */ };
  const handleAddShape = () => { /* 省略 */ };
  const handleDragStart = (event) => { /* ドラッグ開始時の処理 (後述) */ };
  const handleClick = () => { /* カードクリック時の処理 (後述) */ };

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="Canva API Example App"
            description="App name"
          />
        </Text>
        <Button variant="primary" onClick={handleAddText} stretch>
          {intl.formatMessage({
            defaultMessage: 'テキストを追加',
            description: 'テキストを追加',
          })}
        </Button>
        <Button variant="primary" onClick={handleAddShape} stretch>
          {intl.formatMessage({
            defaultMessage: '図形を追加',
            description: '図形を追加',
          })}
        </Button>
        <Rows spacing="1u">
          {/* ImageCardコンポーネントを追加 */}
          <ImageCard
            ariaLabel="Add image to design"
            alt="Example image"
            thumbnailUrl="https://www.canva.dev/example-assets/image-import/image-card-thumbnail.png" // サムネイルURL
            onClick={handleClick} // クリック時の処理 (後述)
            draggable // ドラッグ可能にする
            onDragStart={handleDragStart} // ドラッグ開始時の処理 (後述)
          />
        </Rows>
      </Rows>
    </div>
  );
};
```

`ui.startDragToPoint` API メソッドの第2引数には、ドラッグ&ドロップする要素の情報をオブジェクト形式で指定します。

*   `type: 'image'`:  要素のタイプ (画像要素) を指定
*   `previewUrl`:  ドラッグ中にカーソルに追従するプレビュー画像のURL (publicフォルダからの相対パス)
*   `previewSize`:  プレビュー画像のサイズ (`width`, `height` プロパティ)
*   `resolveImageRef`:  `upload` API メソッドを呼び出し、画像アセットの参照 (ref) を解決する非同期関数

`resolveImageRef` 関数は、`upload` APIメソッドを呼び出し、画像アセットをCanvaにアップロードする処理を記述します。ここでは、`ImageCard`コンポーネントと同じ画像をアップロードするように実装しています。

### 3. クリック時の処理を実装 (handleClick)

`handleClick`関数を実装し、`ImageCard`コンポーネントをクリックした際にCanvaデザインに画像要素を追加する処理を記述します。`addElementAtPoint` APIメソッドを呼び出し、`handleDragStart`関数と同様の要素情報を指定します。

```tsx
const handleDragStart = (event) => { /* 省略 */ };
  const handleClick = async () => {
    await addElementAtPoint({ // addElementAtPoint APIを呼び出し
      type: 'image', // 要素タイプをimageに指定
      element: {
        type: 'image',
        x: 100,
        y: 100,
        width: 300,
        height: 200,
        props: {
          ref: ( // upload APIのレスポンスからrefを取得
            await upload({
              type: 'image',
              url: 'https://www.canva.dev/example-assets/image-import/image-card-preview.png',
              mimeType: 'image/png',
              thumbnailUrl: 'https://www.canva.dev/example-assets/image-import/image-card-thumbnail.png',
              aiDisclosure: 'none',
            })
          ).ref,
        },
      },
    });
  };
```

### 4. 動作確認

Canvaエディタのプレビュー画面で、`ImageCard`コンポーネントをドラッグ&ドロップ、またはクリックし、画像要素がデザインに追加されることを確認します。

*   `ImageCard`コンポーネントをドラッグ&ドロップすると、カーソルにサムネイル画像が追従し、ドロップした位置に画像要素が追加されます。
*   `ImageCard`コンポーネントをクリックすると、Canvasデザインの中央に画像要素が追加されます。


## 外部データ連携 – API経由でコンテンツを取り込む

Canva Apps SDKの大きな魅力の一つは、外部のWeb APIやデータソースと連携し、取得したデータをデザインに反映できることです。この章では、HTTPリクエストを送信して外部APIからデータを取得し、Canva上に動的にコンテンツを表示する方法を解説します。

### 1. 外部APIからのデータ取得 (src/app.tsx)

`src/app.tsx`を編集し、外部API (ここではJSONPlaceholderというAPIを使用) からデータを取得し、コンソール出力する処理を実装します。

```tsx
import React, { useEffect, useState } from 'react'; // useState, useEffect をimport
import { Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';

interface User { // User型定義を追加
  id: number;
  name: string;
  email: string;
}

export const App = () => {
  const intl = useIntl();
  const [users, setUsers] = useState<User[]>([]); // users state を追加

  useEffect(() => { // useEffectフックを追加
    const fetchUsers = async () => {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users'); // JSONPlaceholder API
        if (!response.ok) {
          throw new Error('Failed to fetch users');
        }
        const data: User[] = await response.json(); // レスポンスをJSONとしてparse
        setUsers(data); // users state を更新
        console.log('Users:', data); // 取得したユーザーデータをconsole.logで出力
      } catch (error) {
        console.error('ユーザーデータ取得エラー:', error);
      }
    };

    fetchUsers(); // fetchUsers関数をuseEffect内で呼び出し
  }, []); // useEffectの第2引数に空配列[]を指定 (初回render時のみ実行)

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="Canva API Example App"
            description="App name"
          />
        </Text>
        {/* 後続のUIコンポーネントは省略 */}
      </Rows>
    </div>
  );
};
```

**解説:**

*   `useState<User[]>([]`):  `users` state を定義。API から取得するユーザーデータを格納する配列。初期値は空配列 `[]`。
*   `useEffect(() => { ... }, [])`:  useEffectフックを使用し、初回render時のみAPIリクエストを実行。
*   `fetch('https://jsonplaceholder.typicode.com/users')`:  JSONPlaceholder API (usersエンドポイント) にGETリクエストを送信。
*   `response.ok`:  HTTPレスポンスが成功 (200番台) かどうかをチェック。
*   `response.json()`:  レスポンスbodyをJSON形式でparse。
*   `setUsers(data)`:  取得したユーザーデータを `users` state にset。
*   `console.log('Users:', data)`:  取得したユーザーデータをconsole.logで出力 (デバッグ用)。
*   `catch (error)`:  エラーハンドリング。APIリクエスト失敗時のエラーメッセージをconsole.errorで出力。

### 2. データの表示 (src/app.tsx)

`src/app.tsx`を編集し、取得したユーザーデータをUIに表示します。`users` state をmap関数でloop処理し、`Text`コンポーネントでユーザー名とEmailを表示します。

```tsx
<Rows spacing="2u">
  <Text>
    <FormattedMessage
      defaultMessage="Canva API Example App"
      description="App name"
    />
  </Text>
  {/* 追加: ユーザーデータ表示 */}
  {users.map((user) => (
    <Rows key={user.id} spacing="1u">
      <Text>Name: {user.name}</Text>
      <Text>Email: {user.email}</Text>
    </Rows>
  ))}
  {/* ボタンコンポーネントは省略 */}
</Rows>
```

**解説:**

*   `users.map((user) => ( ... ))`:  `users` state の配列をloop処理し、各ユーザーデータ (`user`) を引数にcallback関数を実行。
*   `<Rows key={user.id} spacing="1u">`:  loop処理で生成する各ユーザー情報 (`Text`コンポーネント) を `Rows` コンポーネントでwrap。`key` prop には `user.id` を指定 (Reactのlist renderingにおけるkeyの指定)。
*   `<Text>Name: {user.name}</Text>`:  ユーザー名 (`user.name`) を表示する `Text` コンポーネント。
*   `<Text>Email: {user.email}</Text>`:  ユーザーEmail (`user.email`) を表示する `Text` コンポーネント。

### 3. 動作確認

Canvaエディタのプレビュー画面で、アプリをreloadし、UIにユーザーデータが表示されることを確認します。

*   アプリ起動時、`useEffect`フックが実行され、JSONPlaceholder API からユーザーデータが取得されます。
*   取得されたユーザーデータが、`Text`コンポーネントを使ってUIにリスト表示されます。

![](/_next/static/media/api-data-app-preview.png)

## 応用編：外部サービス認証と連携 (OAuth)

Canva Apps SDKでは、OAuth 2.0 を利用したユーザー認証を実装し、外部サービス (例: Google Drive) と連携するアプリを開発できます。この章では、OAuth 2.0 認証フローを組み込み、ユーザーがCanvaアプリからGoogle Driveのファイルにアクセスできるようにする方法を解説します。

### 1. OAuth 2.0 認証基盤の準備

OAuth 2.0 連携を実装するには、事前にGoogle Cloud Platform (GCP) でOAuth 2.0 認証基盤を準備する必要があります。

1.  GCPプロジェクトの作成:  [Google Cloud Console](https://console.cloud.google.com/) にアクセスし、新しいプロジェクトを作成します。
2.  OAuth同意画面の設定:  GCPコンソールの「APIとサービス」>「OAuth同意画面」で、アプリ名、ロゴ、サポートメールなどを設定します。
3.  認証情報 (クライアントID, クライアントシークレット) の作成:  GCPコンソールの「APIとサービス」>「認証情報」で、認証情報を作成します。「認証情報を作成」>「OAuthクライアントID」を選択し、アプリケーションの種類 (ウェブアプリケーション) を選択、承認済みのJavaScript生成元と承認済みのリダイレクトURI を登録します。

*   **承認済みの JavaScript 生成元**:  `https://app.canva.com`
*   **承認済みのリダイレクト URI**:  `https://www.canva.com/apps/oauth/authorized`

### 2. Developer Portal でOAuth設定

Canva Developer Portalで、OAuth 2.0 連携に必要な設定を行います。

1.  Developer Portalで、アプリの「認証」>「OAuth連携」ページを開きます。
2.  「OAuth 2.0 クライアント設定」セクションで、GCPで作成した認証情報を入力します。

*   **プロバイダー**:  `Google` を選択
*   **Client ID**:  GCPで作成したクライアントID
*   **Client secret**:  GCPで作成したクライアントシークレット
*   **Authorization server URL**:  `https://accounts.google.com/o/oauth2/v2/auth` (Google OAuth 2.0 認証エンドポイント)
*   **Token exchange URL**:  `https://oauth2.googleapis.com/token` (Google OAuth 2.0 トークンエンドポイント)
*   **Revocation exchange URL (Optional)**:  `https://oauth2.googleapis.com/revoke` (Google OAuth 2.0 トークンrevokeエンドポイント)
*   **PKCE**:  `オン` を選択 (PKCEを有効化)

### 3. コード実装 (src/app.tsx)

`src/app.tsx`を編集し、OAuth 2.0 認証フローを実装します。

```tsx
import React, { useEffect, useState, useCallback } from 'react';
import { Button, Rows, Text } from '@canva/app-ui-kit';
import { FormattedMessage, useIntl } from 'react-intl';
import * as styles from 'styles/components.css';
import { auth } from '@canva/user'; // auth API をimport

const oauthClient = auth.initOauth(); // OAuth client を初期化

export const App = () => {
  const intl = useIntl();
  const [isAuthorized, setIsAuthorized] = useState(false); // 認証状態 state を追加

  const handleAuth = async () => { // 認証処理
    const authResult = await oauthClient.requestAuthorization(); // 認証リクエスト開始
    if (authResult.status === 'completed') {
      setIsAuthorized(true); // 認証成功 -> 認証状態 state をtrueに
    } else {
      console.error('OAuth failed', authResult.status); // 認証失敗 -> エラーログ出力
    }
  };

  useEffect(() => { // useEffectフックを追加 (初回render時に認証状態をチェック)
    const checkAuthStatus = async () => {
      const token = await oauthClient.getAccessToken(); // アクセストークンを取得
      setIsAuthorized(!!token); // アクセストークンが存在すれば認証済と判定
    };
    checkAuthStatus();
  }, []);

  const handleLogout = async () => { // ログアウト処理
    await oauthClient.deauthorize(); // 認可解除
    setIsAuthorized(false); // 認証状態 state をfalseに
  };

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="2u">
        <Text>
          <FormattedMessage
            defaultMessage="Canva API Example App"
            description="App name"
          />
        </Text>
        {/* 追加: 認証状態によってUIを切り替え */}
        {isAuthorized ? (
          <>
            <Text>Google Drive authorized</Text>
            <Button variant="secondary" onClick={handleLogout} stretch>
              {intl.formatMessage({
                defaultMessage: 'ログアウト', // ログアウト
                description: 'ログアウト',
              })}
            </Button>
            {/* TODO: Google Driveのファイル一覧を表示するUIを実装 */}
          </>
        ) : (
          <Button variant="primary" onClick={handleAuth} stretch>
            {intl.formatMessage({
              defaultMessage: 'Google Driveと連携', // Google Driveと連携
              description: 'Google Driveと連携',
            })}
          </Button>
        )}
      </Rows>
    </div>
  );
};
```

**解説:**

*   `auth.initOauth()`:  OAuth client を初期化。
*   `useState(false)`:  `isAuthorized` state を定義。認証状態を管理 (初期値は `false` = 未認証)。
*   `oauthClient.requestAuthorization()`:  OAuth 2.0 認証フローを開始。Canva 認証画面がポップアップ表示され、ユーザーにGoogleアカウントでのログインとアプリ連携の許可を求めます。
*   `oauthClient.getAccessToken()`:  アクセストークンを取得。認証済かどうかをチェックするために初回render時に実行。
*   `oauthClient.deauthorize()`:  認可解除 (ログアウト) 処理。
*   `isAuthorized ? (...) : (...)`:  `isAuthorized` state の値によってUIを切り替え。
    *   認証済 (`true`):  「Google Drive authorized」と表示し、ログアウトボタンを表示。
    *   未認証 (`false`):  「Google Driveと連携」ボタンを表示。

### 4. 動作確認

Canvaエディタのプレビュー画面で、アプリをreloadし、動作を確認します。

1.  「Google Driveと連携」ボタンをクリックします。
2.  Canvaの認証ポップアップウィンドウが開き、Googleアカウントへのログインとアプリ連携の許可を求められます。
3.  Googleアカウントでログインし、アプリ連携を許可します。
4.  アプリの表示が「Google Drive authorized」に変わり、ログアウトボタンが表示されることを確認します。
5.  「ログアウト」ボタンをクリックすると、未認証状態に戻ることを確認します。

## Canva API 開発で遭遇する課題と解決策：エラーハンドリング、レート制限対策、セキュリティ強化 (デバッグテクニック、API 使用量最適化、ベストプラクティス)

Canva API 開発では、エラーハンドリング、レート制限対策、セキュリティ強化が重要な課題となります。

**独自の視点:**

単にエラーメッセージを表示するだけでなく、**エラーの種類に応じて異なる対応**を検討しましょう。例えば、API リクエスト制限エラーの場合は、バックオフ戦略を適用し、リトライキューにリクエストを追加します。認証エラーの場合は、リフレッシュトークンを使用してアクセストークンを再取得し、ユーザーに再認証を促す必要があるかどうかを判断します。

**エラーハンドリングの例:**

```javascript
async function handleCanvaAPIRequest(apiCall) {
  try {
    const response = await apiCall();
    return response.data;
  } catch (error) {
    if (error.response && error.response.status === 429) {
      // レート制限エラー
      console.warn('レート制限超過: バックオフ戦略を適用します');
      // バックオフ処理 (指数関数的な遅延)
      await new Promise(resolve => setTimeout(resolve, backoffDelay));
      backoffDelay = backoff_delay * 2;
      return handleCanvaAPIRequest(apiCall); // リトライ
    } else if (error.response && error.response.status === 401) {
      // 認証エラー
      console.error('認証エラー: リフレッシュトークンを試行します');
      // リフレッシュトークンでアクセストークンを再取得
      const newAccessToken = await refreshAccessToken();
      if (newAccessToken) {
        // 新しいアクセストークンを設定
        // ...
        return handleCanvaAPIRequest(apiCall); // リトライ
      } else {
        // リフレッシュトークンも無効 -> ユーザーに再認証を促す
        console.error('リフレッシュトークンも無効: ユーザーに再認証を促してください');
        throw error;
      }
    } else {
      console.error('Canva API エラー:', error.response ? error.response.data : error.message);
      throw error;
    }
  }
}
```

**セキュリティ強化:**

*   API キーは環境変数で管理し、ソースコードに直接記述しない。
*   API リクエスト時に、サーバーサイドで入力値を検証し、不正なデータを受け付けない。
*   HTTPS を使用して、API リクエストを暗号化する。
*   定期的に API キーをローテーションする。

## 応用編：チームコラボレーションを加速するCanvaアプリ開発 (リアルタイム共同編集機能、レビュー承認ワークフロー、Canva プラグイン開発入門)

Canva プラグイン (Canva Apps) を開発することで、Canva の機能を拡張し、チームコラボレーションを加速することができます。

**独自の視点:**

Canva プラグイン開発では、単に既存の機能をラップするだけでなく、**Canva の UI/UX に溶け込むような、自然な操作感**を実現することが重要です。Canva のデザインシステムを理解し、それに沿った UI コンポーネントを使用することで、ユーザーエクスペリエンスを向上させることができます。

**例：レビュー承認ワークフロープラグイン:**

1.  デザインが完成したら、プラグインを通じてレビューを依頼。
2.  レビュー担当者は、Canva 上で直接コメントや修正指示を追加。
3.  デザイナーは、レビューコメントを元にデザインを修正。
4.  レビュー担当者が承認したら、デザインを公開またはダウンロード。

**Canva プラグイン開発入門:**

Canva プラグイン開発には、Canva Apps SDK を使用します。SDK には、UI コンポーネント、API クライアント、認証ライブラリなどが含まれています。

**ポイント:**

*   Canva のデザインシステムを理解し、UI/UX を最適化する。
*   Canva Apps SDK を活用して、効率的な開発を行う。
*   ユーザーからのフィードバックを積極的に収集し、プラグインを改善する。

この記事では、Canva API を活用して業務効率を劇的に向上させるための、実践的なレシピを紹介しました。これらのテクニックを応用することで、Canva の可能性を最大限に引き出し、創造的なワークフローを構築することができます。

[^1]: 2024年5月2日 BUSINESS WIRE “Canvaがデザインの未来を創造” ([https://www.businesswire.com/news/home/20240502249158/ja/](https://www.businesswire.com/news/home/20240502249158/ja/))