mcpField
U
umbraco-openapi-client

umbraco-openapi-client

github.com/umbraco開発27397 回インストール6 週間前に更新
リポジトリ

umbraco-openapi-client について

Umbraco のバックオフィス拡張からカスタム API を呼び出す開発者向けの実装ガイドです。型付き TypeScript OpenAPI クライアントの生成と、Umbraco の認証コンテキストを使った設定を扱います。

主な機能

  1. 01TypeScript OpenAPI クライアントの生成
  2. 02Umbraco のベアラートークン認証設定
  3. 03C# モデルからの型生成
  4. 04クライアント設定と認証手順の解説
  5. 05よくある認証ミスの確認

活用シーン

  1. 01バックオフィス UI からカスタム API を呼び出す
  2. 02ツリーやワークスペースに API データを読み込む
  3. 03カスタムコントローラー用クライアントを生成する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-openapi-client

インストール後、Claude Code で/umbraco-openapi-clientを入力して読み込まれたか確認します。

詳細

Umbraco OpenAPI クライアントのセットアップ

重要: これが重要な理由

Umbraco バックオフィス API との通信に生の fetch() 呼び出しを決して使用しないでください。 生の fetch 呼び出しは、Umbraco が要求するベアラートークン認証を含まないため、401 Unauthorized エラーになります。

常に Umbraco の認証コンテキストで構成された生成済みの OpenAPI クライアントを使用してください。 これにより以下が保証されます。

  • 適切なベアラートークン認証
  • 型安全な API 呼び出し
  • トークン更新の自動処理

これを使用する場面

以下のいずれかに該当する場合は常にこのパターンを使用してください。

  • [BackOfficeRoute] を付けたカスタム C# API コントローラーを作成する場合
  • バックオフィスのフロントエンドからカスタム API を呼び出す必要がある場合
  • カスタムエンドポイントからデータを読み込むツリー、ワークスペース、その他の UI を構築する場合

セットアップの概要

セットアップは 4 つの部分から構成されます。

  1. C# バックエンド: Swagger/OpenAPI ドキュメントを持つコントローラー
  2. クライアントの依存関係: @hey-api/openapi-ts(@hey-api/client-fetch プラグインはこれに同梱されています)
  3. 生成スクリプト: swagger.json を取得して TypeScript クライアントを生成します
  4. エントリポイントの構成: クライアントを Umbraco 認証で構成します

手順ごとの実装

1. C# バックエンドのセットアップ(Swagger/OpenAPI)

API はバックオフィスの OpenAPI ドキュメントとして公開する必要があります。コンポーザーを作成します。

Umbraco 18 以降: OpenAPI スタックは Swashbuckle の SwaggerGenOptions (および BackOfficeSecurityRequirementsOperationFilterBase / OperationIdHandler 型)から、流暢な AddBackOfficeOpenApiDocument(...) ビルダーへ移行しました。以下 のパターンを使用してください。以前の SwaggerGenOptions のアプローチは v18 では コンパイルできなくなりました。

// Composers/MyApiComposer.cs
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Cms.Api.Management.OpenApi;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;

namespace MyExtension.Composers;

public class MyApiComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder) =>

        // Registers a dedicated backoffice OpenAPI document, served at
        // /umbraco/openapi/{ApiName}.json and browsable via Swagger UI.
        // See https://docs.umbraco.com/umbraco-cms/extend-your-project/tutorials/creating-a-backoffice-api
        builder.AddBackOfficeOpenApiDocument(
            Constants.ApiName,
            document => document
                .WithTitle("My Extension API")
                .WithBackOfficeAuthentication()
                .WithJsonOptions(Umbraco.Cms.Core.Constants.JsonOptionsNames.BackOffice)
                .ConfigureOpenApiOptions(options =>
                    options.AddDocumentTransformer((doc, _, _) =>
                    {
                        doc.Info.Version = "1.0";
                        return Task.CompletedTask;
                    })));
}

// Constants
public static class Constants
{
    public const string ApiName = "myextension";
}
2. クライアントの package.json 依存関係

Client/package.json に追加します。

{
  "scripts": {
    "generate-client": "node scripts/generate-openapi.js https://localhost:44325/umbraco/openapi/myextension.json"
  },
  "devDependencies": {
    "@hey-api/openapi-ts": "^0.97.0",
    "chalk": "^5.4.1",
    "node-fetch": "^3.3.2"
  }
}
3. 生成スクリプト

Client/scripts/generate-openapi.js を作成します。

import fetch from "node-fetch";
import chalk from "chalk";
import { createClient, defaultPlugins } from "@hey-api/openapi-ts";

console.log(chalk.green("Generating OpenAPI client..."));

const swaggerUrl = process.argv[2];
if (swaggerUrl === undefined) {
  console.error(chalk.red(`ERROR: Missing URL to OpenAPI spec`));
  process.exit(1);
}

// Ignore self-signed certificates on localhost
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

console.log(`Fetching OpenAPI definition from ${chalk.yellow(swaggerUrl)}`);

fetch(swaggerUrl)
  .then(async (response) => {
    if (!response.ok) {
      console.error(chalk.red(`ERROR: ${response.status} ${response.statusText}`));
      return;
    }

    await createClient({
      input: swaggerUrl,
      output: "src/api",
      plugins: [
        ...defaultPlugins,
        {
          name: "@hey-api/client-fetch",
          bundle: true,
          exportFromIndex: true,
          throwOnError: true,
        },
        {
          name: "@hey-api/typescript",
          enums: "typescript",
        },
        {
          name: "@hey-api/sdk",
          asClass: true,
        },
      ],
    });

    console.log(chalk.green("Client generated successfully!"));
  })
  .catch((error) => {
    console.error(`ERROR: ${chalk.red(error.message)}`);
  });
4. エントリポイントの構成(重要)

エントリポイントで、生成されたクライアントを Umbraco の認証コンテキストで構成します。

// src/entrypoints/entrypoint.ts
import type { UmbEntryPointOnInit, UmbEntryPointOnUnload } from "@umbraco-cms/backoffice/extension-api";
import { UMB_AUTH_CONTEXT } from "@umbraco-cms/backoffice/auth";
import { client } from "../api/client.gen.js";

export const onInit: UmbEntryPointOnInit = (host, _extensionRegistry) => {
  // CRITICAL: Configure the OpenAPI client with authentication
  host.consumeContext(UMB_AUTH_CONTEXT, (authContext) => {
    if (!authContext) return;

    // configureClient() sets baseUrl/credentials/bearer token AND binds the
    // default interceptors (401 retry, problem-details error notifications).
    // It guards against configuring the same client twice, so it's safe to
    // call for umbHttpClient and your own generated client alike.
    authContext.configureClient(client);

    console.log("API client configured with auth");
  });
};

export const onUnload: UmbEntryPointOnUnload = (_host, _extensionRegistry) => {
  // Cleanup if needed
};
手動による代替方法(デフォルトのインターセプターをスキップする)

クライアント構成を完全に制御する必要があり、デフォルトの インターセプターを必要としない場合は、代わりにクライアントを手動で構成できます。

const config = authContext.getOpenApiConfiguration();

client.setConfig({
  baseUrl: config.base,
  credentials: config.credentials,
  auth: config.token,  // This provides the bearer token!
});

これは正しく認証されますが、configureClient() が組み込む 401 リトライと problem-details のエラー処理が失われます。また、認証をブラックボックスとして扱う のではなく、現在のベアラートークンの仕組みに結合されることになります。

5. 生成されたクライアントの使用

npm run generate-client を実行した後、生成されたサービスを使用します。

// In your workspace context, repository, or data source
import { MyExtensionService } from "../api/index.js";

// The client handles auth automatically!
const response = await MyExtensionService.getItems({
  query: { skip: 0, take: 50 },
});

const item = await MyExtensionService.getItem({
  path: { id: "some-guid" },
});

await MyExtensionService.createItem({
  body: { name: "New Item", value: 123 },
});

生成ワークフロー

  1. Umbraco を起動 - swagger.json エンドポイントにアクセスできる必要があります
  2. 生成を実行: npm run generate-client
  3. 生成されたファイルは src/api/ に出力されます。
    • types.gen.ts - C# モデルから生成された TypeScript 型
    • sdk.gen.ts - 型付きメソッドを持つサービスクラス
    • client.gen.ts - HTTP クライアントの構成
    • index.ts - すべてを再エクスポート

よくある間違い

❌ 間違い: 生の fetch
// This will get 401 Unauthorized!
const response = await fetch('/umbraco/myextension/api/v1/items');
❌ 間違い: credentials のみを指定した fetch
// Still fails - cookies don't work for Management API
const response = await fetch('/umbraco/myextension/api/v1/items', {
  credentials: 'include'
});
✅ 正しい: 生成された OpenAPI クライアント
// Client is configured with bearer token in entry point
const response = await MyExtensionService.getItems();

参考例

以下で完全な動作する実装を確認できます。

  • examples/notes-wiki/Client/ - 完全な OpenAPI クライアントのセットアップ
  • examples/tree-example/Client/ - OpenAPI 統合を備えたツリー

作成する主要ファイル

  1. Composers/MyApiComposer.cs - Swagger の登録
  2. Client/scripts/generate-openapi.js - 生成スクリプト
  3. Client/src/entrypoints/entrypoint.ts - 認証の構成
  4. Client/src/api/ - 生成済み(手動で編集しないでください)

以上です! 常に API クライアントを生成し、認証で構成してください。認証が必要なエンドポイントに生の fetch を決して使用しないでください。

コメント