typespec-api-operations

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Add TypeSpec API Operations

为TypeSpec API添加操作

Add RESTful operations to an existing TypeSpec API plugin for Microsoft 365 Copilot.
为Microsoft 365 Copilot的现有TypeSpec API插件添加RESTful操作。

Adding GET Operations

添加GET操作

Simple GET - List All Items

简单GET - 列出所有条目

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

GET with Query Parameter - Filter Results

带查询参数的GET - 过滤结果

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

GET with Path Parameter - Get Single Item

带路径参数的GET - 获取单个条目

typescript
/**
 * 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;
typescript
/**
 * 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;

GET with Adaptive Card

带自适应卡片的GET

typescript
/**
 * List items with adaptive card visualization.
 */
@route("/items")
@card(#{
  dataPath: "$",
  title: "$.title",
  file: "item-card.json"
})
@get op listItems(): Item[];
Create the Adaptive Card (
appPackage/item-card.json
):
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}"
    }
  ]
}
typescript
/**
 * List items with adaptive card visualization.
 */
@route("/items")
@card(#{
  dataPath: "$",
  title: "$.title",
  file: "item-card.json"
})
@get op listItems(): Item[];
创建自适应卡片 (
appPackage/item-card.json
):
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}"
    }
  ]
}

Adding POST Operations

添加POST操作

Simple POST - Create Item

简单POST - 创建条目

typescript
/**
 * 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;
}
typescript
/**
 * 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 with Confirmation

带确认机制的POST

typescript
/**
 * 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;
typescript
/**
 * 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;

Adding PATCH Operations

添加PATCH操作

Simple PATCH - Update Item

简单PATCH - 更新条目

typescript
/**
 * 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";
}
typescript
/**
 * 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 with Confirmation

带确认机制的PATCH

typescript
/**
 * 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;
typescript
/**
 * 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;

Adding DELETE Operations

添加DELETE操作

Simple DELETE

简单DELETE

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

DELETE with Confirmation

带确认机制的DELETE

typescript
/**
 * 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;
typescript
/**
 * 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;

Complete CRUD Example

完整CRUD示例

Define the Service and Models

定义服务与模型

typescript
@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;
}
typescript
@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;
}

Advanced Features

高级功能

Multiple Query Parameters

多查询参数

typescript
@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;
}
typescript
@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 Parameters

请求头参数

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

Custom Response Models

自定义响应模型

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

model DeleteResponse {
  success: boolean;
  message: string;
  deletedId: integer;
}
typescript
@route("/items/{id}")
@delete op deleteItem(@path id: integer): DeleteResponse;

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

Error Responses

错误响应

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

@route("/items/{id}")
@get op getItem(@path id: integer): Item | ErrorResponse;
typescript
model ErrorResponse {
  error: {
    code: string;
    message: string;
    details?: string[];
  };
}

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

Testing Prompts

测试提示

After adding operations, test with these prompts:
GET Operations:
  • "List all items and show them in a table"
  • "Show me items for user ID 1"
  • "Get the details of item 42"
POST Operations:
  • "Create a new item with title 'My Task' for user 1"
  • "Add an item: title 'New Feature', description 'Add login'"
PATCH Operations:
  • "Update item 10 with title 'Updated Title'"
  • "Change the status of item 5 to completed"
DELETE Operations:
  • "Delete item 99"
  • "Remove the item with ID 15"
添加操作后,使用以下提示进行测试:
GET操作测试:
  • "List all items and show them in a table"
  • "Show me items for user ID 1"
  • "Get the details of item 42"
POST操作测试:
  • "Create a new item with title 'My Task' for user 1"
  • "Add an item: title 'New Feature', description 'Add login'"
PATCH操作测试:
  • "Update item 10 with title 'Updated Title'"
  • "Change the status of item 5 to completed"
DELETE操作测试:
  • "Delete item 99"
  • "Remove the item with ID 15"

Best Practices

最佳实践

Parameter Naming

参数命名

  • Use descriptive parameter names:
    userId
    not
    uid
  • Be consistent across operations
  • Use optional parameters (
    ?
    ) for filters
  • 使用具有描述性的参数名称:
    userId
    而非
    uid
  • 保持所有操作的参数命名一致性
  • 对筛选条件使用可选参数(
    ?

Documentation

文档编写

  • Add JSDoc comments to all operations
  • Describe what each parameter does
  • Document expected responses
  • 为所有操作添加JSDoc注释
  • 描述每个参数的作用
  • 记录预期的响应结果

Models

模型设计

  • Use
    @visibility(Lifecycle.Read)
    for read-only fields like
    id
  • Use
    @format("date-time")
    for date fields
  • Use union types for enums:
    "active" | "completed"
  • Make optional fields explicit with
    ?
  • id
    等只读字段使用
    @visibility(Lifecycle.Read)
  • 对日期字段使用
    @format("date-time")
  • 使用联合类型定义枚举值:
    "active" | "completed"
  • ?
    明确标记可选字段

Confirmations

确认机制

  • Always add confirmations to destructive operations (DELETE, PATCH)
  • Show key details in confirmation body
  • Use warning emoji (⚠️) for irreversible actions
  • 对DELETE、PATCH等破坏性操作始终添加确认机制
  • 在确认内容中显示关键信息
  • 对不可逆操作使用警告表情(⚠️)

Adaptive Cards

自适应卡片

  • Keep cards simple and focused
  • Use conditional rendering with
    ${if(..., ..., 'N/A')}
  • Include action buttons for common next steps
  • Test data binding with actual API responses
  • 保持卡片简洁且聚焦核心信息
  • 使用
    ${if(..., ..., 'N/A')}
    实现条件渲染
  • 包含常用后续操作的按钮
  • 结合实际API响应测试数据绑定

Routing

路由设计

  • Use RESTful conventions:
    • GET /items
      - List
    • GET /items/{id}
      - Get one
    • POST /items
      - Create
    • PATCH /items/{id}
      - Update
    • DELETE /items/{id}
      - Delete
  • Group related operations in the same namespace
  • Use nested routes for hierarchical resources
  • 遵循RESTful规范:
    • GET /items
      - 列出所有条目
    • GET /items/{id}
      - 获取单个条目
    • POST /items
      - 创建条目
    • PATCH /items/{id}
      - 更新条目
    • DELETE /items/{id}
      - 删除条目
  • 将相关操作归到同一命名空间
  • 对层级化资源使用嵌套路由

Common Issues

常见问题排查

Issue: Parameter not showing in Copilot

问题:参数未在Copilot中显示

Solution: Check parameter is properly decorated with
@query
,
@path
, or
@body
解决方案: 检查参数是否正确使用
@query
@path
@body
装饰器

Issue: Adaptive card not rendering

问题:自适应卡片未渲染

Solution: Verify file path in
@card
decorator and check JSON syntax
解决方案: 验证
@card
装饰器中的文件路径,并检查JSON语法是否正确

Issue: Confirmation not appearing

问题:确认机制未触发

Solution: Ensure
@capabilities
decorator is properly formatted with confirmation object
解决方案: 确保
@capabilities
装饰器中的确认对象格式正确

Issue: Model property not appearing in response

问题:模型属性未在响应中显示

Solution: Check if property needs
@visibility(Lifecycle.Read)
or remove it if it should be writable
解决方案: 检查该字段是否需要添加
@visibility(Lifecycle.Read)
,若为可写字段则移除该装饰器