typespec-api-operations

typespec-api-operations

熱門

為 TypeSpec API 外掛模組新增 GET、POST、PATCH 及 DELETE 操作,包含適當的路由設定、參數配置與 Adaptive Card 展示

3.7萬星標
4569分支
更新於 2026/7/14
SKILL.md
唯讀
名稱
typespec-api-operations
描述

為 TypeSpec API 外掛模組新增 GET、POST、PATCH 及 DELETE 操作,包含適當的路由設定、參數配置與 Adaptive Card 展示

新增 TypeSpec API 操作

為現有的 Microsoft 365 Copilot TypeSpec API 外掛模組新增 RESTful 操作。

新增 GET 操作

簡單 GET — 列出所有項目

/**
 * List all items.
 */
@route("/items")
@get op listItems(): Item[];

帶 Query 參數的 GET — 篩選結果

/**
 * List items filtered by criteria.
 * @param userId Optional user ID to filter items
 */
@route("/items")
@get op listItems(@query userId?: integer): Item[];

帶 Path 參數的 GET — 取得單一項目

/**
 * Get a specific item by ID.
 * @param id The ID of the item to retrieve
 */
@route("/items/{id}")
@get op getItem(@path id: integer): Item;

搭配 Adaptive Card 的 GET

/**
 * List items with adaptive card visualization.
 */
@route("/items")
@card(#{
  dataPath: "$",
  title: "$.title",
  file: "item-card.json"
})
@get op listItems(): Item[];

建立 Adaptive Card (appPackage/item-card.json):

{
  "type": "AdaptiveCard",
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5",
  "body": [
    {
      "type": "Container",
      "$data": "${$root}",
      "items": [
        {
          "type": "TextBlock",
          "text": "**${if(title, title, 'N/A')}**",
          "wrap": true
        },
        {
          "type": "TextBlock",
          "text": "${if(description, description, 'N/A')}",
          "wrap": true
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "View Details",
      "url": "https://example.com/items/${id}"
    }
  ]
}

新增 POST 操作

簡單 POST — 建立項目

/**
 * Create a new item.
 * @param item The item to create
 */
@route("/items")
@post op createItem(@body item: CreateItemRequest): Item;

model CreateItemRequest {
  title: string;
  description?: string;
  userId: integer;
}

帶有確認機制的 POST

/**
 * Create a new item with confirmation.
 */
@route("/items")
@post
@capabilities(#{
  confirmation: #{
    type: "AdaptiveCard",
    title: "Create Item",
    body: """
    Are you sure you want to create this item?
      * **Title**: {{ function.parameters.item.title }}
      * **User ID**: {{ function.parameters.item.userId }}
    """
  }
})
op createItem(@body item: CreateItemRequest): Item;

新增 PATCH 操作

簡單 PATCH — 更新項目

/**
 * Update an existing item.
 * @param id The ID of the item to update
 * @param item The updated item data
 */
@route("/items/{id}")
@patch op updateItem(
  @path id: integer,
  @body item: UpdateItemRequest
): Item;

model UpdateItemRequest {
  title?: string;
  description?: string;
  status?: "active" | "completed" | "archived";
}

帶有確認機制的 PATCH

/**
 * Update an item with confirmation.
 */
@route("/items/{id}")
@patch
@capabilities(#{
  confirmation: #{
    type: "AdaptiveCard",
    title: "Update Item",
    body: """
    Updating item #{{ function.parameters.id }}:
      * **Title**: {{ function.parameters.item.title }}
      * **Status**: {{ function.parameters.item.status }}
    """
  }
})
op updateItem(
  @path id: integer,
  @body item: UpdateItemRequest
): Item;

新增 DELETE 操作

簡單 DELETE

/**
 * Delete an item.
 * @param id The ID of the item to delete
 */
@route("/items/{id}")
@delete op deleteItem(@path id: integer): void;

帶有確認機制的 DELETE

/**
 * Delete an item with confirmation.
 */
@route("/items/{id}")
@delete
@capabilities(#{
  confirmation: #{
    type: "AdaptiveCard",
    title: "Delete Item",
    body: """
    ⚠️ Are you sure you want to delete item #{{ function.parameters.id }}?
    This action cannot be undone.
    """
  }
})
op deleteItem(@path id: integer): void;

完整 CRUD 範例

定義服務與模型

@service
@server("https://api.example.com")
@actions(#{
  nameForHuman: "Items API",
  descriptionForHuman: "Manage items",
  descriptionForModel: "Read, create, update, and delete items"
})
namespace ItemsAPI {
  
  // Models
  model Item {
    @visibility(Lifecycle.Read)
    id: integer;
    
    userId: integer;
    title: string;
    description?: string;
    status: "active" | "completed" | "archived";
    
    @format("date-time")
    createdAt: utcDateTime;
    
    @format("date-time")
    updatedAt?: utcDateTime;
  }

  model CreateItemRequest {
    userId: integer;
    title: string;
    description?: string;
  }

  model UpdateItemRequest {
    title?: string;
    description?: string;
    status?: "active" | "completed" | "archived";
  }

  // Operations
  @route("/items")
  @card(#{ dataPath: "$", title: "$.title", file: "item-card.json" })
  @get op listItems(@query userId?: integer): Item[];

  @route("/items/{id}")
  @card(#{ dataPath: "$", title: "$.title", file: "item-card.json" })
  @get op getItem(@path id: integer): Item;

  @route("/items")
  @post
  @capabilities(#{
    confirmation: #{
      type: "AdaptiveCard",
      title: "Create Item",
      body: "Creating: **{{ function.parameters.item.title }}**"
    }
  })
  op createItem(@body item: CreateItemRequest): Item;

  @route("/items/{id}")
  @patch
  @capabilities(#{
    confirmation: #{
      type: "AdaptiveCard",
      title: "Update Item",
      body: "Updating item #{{ function.parameters.id }}"
    }
  })
  op updateItem(@path id: integer, @body item: UpdateItemRequest): Item;

  @route("/items/{id}")
  @delete
  @capabilities(#{
    confirmation: #{
      type: "AdaptiveCard",
      title: "Delete Item",
      body: "⚠️ Delete item #{{ function.parameters.id }}?"
    }
  })
  op deleteItem(@path id: integer): void;
}

進階功能

多個 Query 參數

@route("/items")
@get op listItems(
  @query userId?: integer,
  @query status?: "active" | "completed" | "archived",
  @query limit?: integer,
  @query offset?: integer
): ItemList;

model ItemList {
  items: Item[];
  total: integer;
  hasMore: boolean;
}

Header 參數

@route("/items")
@get op listItems(
  @header("X-API-Version") apiVersion?: string,
  @query userId?: integer
): Item[];

自訂回應模型

@route("/items/{id}")
@delete op deleteItem(@path id: integer): DeleteResponse;

model DeleteResponse {
  success: boolean;
  message: string;
  deletedId: integer;
}

錯誤回應

model ErrorResponse {
  error: {
    code: string;
    message: string;
    details?: string[];
  };
}

@route("/items/{id}")
@get op getItem(@path id: integer): Item | ErrorResponse;

測試提示詞

新增操作後,可以使用以下提示詞進行測試:

GET 操作:

  • 「列出所有項目並以表格顯示」
  • 「顯示使用者 ID 為 1 的項目」
  • 「取得項目 42 的詳細資料」

POST 操作:

  • 「為使用者 1 建立一個標題為『My Task』的新項目」
  • 「新增一個項目:標題『New Feature』,描述『Add login』」

PATCH 操作:

  • 「將項目 10 的標題更新為『Updated Title』」
  • 「將項目 5 的狀態變更為已完成 (completed)」

DELETE 操作:

  • 「刪除項目 99」
  • 「移除 ID 為 15 的項目」

最佳實踐

參數命名

  • 使用具描述性的參數名稱:使用 userId 而非 uid
  • 在各個操作之間保持一致
  • 使用可選參數 (?) 作為篩選條件

文件說明

  • 為所有操作新增 JSDoc 註解
  • 說明每個參數的作用
  • 記錄預期的回應格式

模型

  • 唯讀欄位(如 id)請使用 @visibility(Lifecycle.Read)
  • 日期欄位請使用 @format("date-time")
  • 列舉 (Enums) 請使用聯合類型 (Union types):"active" | "completed"
  • 可選欄位請明確標示 ?

確認機制

  • 務必為破壞性操作 (DELETE、PATCH) 新增確認機制
  • 在確認訊息主體中顯示關鍵細節
  • 對於不可逆的操作,使用警告 Emoji (⚠️)

Adaptive Cards

  • 保持卡片設計簡潔且主題明確
  • 使用 ${if(..., ..., 'N/A')} 進行條件式渲染
  • 包含常用後續步驟的動作按鈕 (Action buttons)
  • 使用真實的 API 回應資料測試資料繫結 (Data binding)

路由設定

  • 遵循 RESTful 設計規範:
    • GET /items — 清單列表
    • GET /items/{id} — 取得單一項目
    • POST /items — 建立項目
    • PATCH /items/{id} — 更新項目
    • DELETE /items/{id} — 刪除項目
  • 將相關操作歸類在同一個命名空間 (Namespace)
  • 階層式資源請使用巢狀路由

常見問題

問題:參數未出現在 Copilot 中

解決方案:檢查參數是否已正確加上 @query@path@body 裝飾器

問題:Adaptive Card 無法渲染

解決方案:確認 @card 裝飾器中的檔案路徑,並檢查 JSON 語法

問題:確認畫面未顯示

解決方案:確保 @capabilities 裝飾器中的確認物件格式正確

問題:模型屬性未出現在回應中

解決方案:檢查屬性是否需要加上 @visibility(Lifecycle.Read),或者若屬性應為可寫入則將其移除