typespec-api-operations

Par github · awesome-copilot

Ajoutez des opérations GET, POST, PATCH et DELETE à un plugin API TypeSpec avec un routage, des paramètres et des cartes adaptatives appropriés

npx skills add https://github.com/github/awesome-copilot --skill typespec-api-operations

Ajouter des opérations API TypeSpec

Ajoutez des opérations RESTful à un plugin API TypeSpec existant pour Microsoft 365 Copilot.

Ajouter des opérations GET

GET simple - Lister tous les éléments

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

GET avec paramètre de requête - Filtrer les résultats

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

GET avec paramètre de chemin - Obtenir un élément unique

/**
 * 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 avec Adaptive Card

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

Créez l'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}"
    }
  ]
}

Ajouter des opérations POST

POST simple - Créer un élément

/**
 * 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 avec confirmation

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

Ajouter des opérations PATCH

PATCH simple - Mettre à jour un élément

/**
 * 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 avec confirmation

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

Ajouter des opérations DELETE

DELETE simple

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

DELETE avec confirmation

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

Exemple CRUD complet

Définir le service et les modèles

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

Fonctionnalités avancées

Plusieurs paramètres de requête

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

Paramètres d'en-tête

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

Modèles de réponse personnalisés

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

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

Réponses d'erreur

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

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

Prompts de test

Après l'ajout d'opérations, testez avec ces prompts :

Opérations GET :

  • « Lister tous les éléments et les afficher dans un tableau »
  • « Affiche-moi les éléments pour l'ID utilisateur 1 »
  • « Obtiens les détails de l'élément 42 »

Opérations POST :

  • « Créer un nouvel élément avec le titre 'Ma tâche' pour l'utilisateur 1 »
  • « Ajoute un élément : titre 'Nouvelle fonctionnalité', description 'Ajouter la connexion' »

Opérations PATCH :

  • « Mets à jour l'élément 10 avec le titre 'Titre mis à jour' »
  • « Change le statut de l'élément 5 à complété »

Opérations DELETE :

  • « Supprime l'élément 99 »
  • « Supprime l'élément avec l'ID 15 »

Bonnes pratiques

Nommage des paramètres

  • Utilisez des noms de paramètres explicites : userId au lieu de uid
  • Soyez cohérent entre les opérations
  • Utilisez des paramètres optionnels (?) pour les filtres

Documentation

  • Ajoutez des commentaires JSDoc à toutes les opérations
  • Décrivez ce que fait chaque paramètre
  • Documentez les réponses attendues

Modèles

  • Utilisez @visibility(Lifecycle.Read) pour les champs en lecture seule comme id
  • Utilisez @format("date-time") pour les champs de date
  • Utilisez les types union pour les énumérations : "active" | "completed" | "archived"
  • Rendez explicites les champs optionnels avec ?

Confirmations

  • Ajoutez toujours des confirmations aux opérations destructrices (DELETE, PATCH)
  • Affichez les détails clés dans le corps de la confirmation
  • Utilisez l'emoji d'avertissement (⚠️) pour les actions irréversibles

Adaptive Cards

  • Gardez les cartes simples et ciblées
  • Utilisez le rendu conditionnel avec ${if(..., ..., 'N/A')}
  • Incluez des boutons d'action pour les prochaines étapes courantes
  • Testez la liaison de données avec les réponses API réelles

Routage

  • Utilisez les conventions RESTful :
    • GET /items - Lister
    • GET /items/{id} - Obtenir un
    • POST /items - Créer
    • PATCH /items/{id} - Mettre à jour
    • DELETE /items/{id} - Supprimer
  • Regroupez les opérations connexes dans le même namespace
  • Utilisez les routes imbriquées pour les ressources hiérarchiques

Problèmes courants

Problème : Le paramètre n'apparaît pas dans Copilot

Solution : Vérifiez que le paramètre est correctement décoré avec @query, @path ou @body

Problème : L'Adaptive Card ne s'affiche pas

Solution : Vérifiez le chemin du fichier dans le décorateur @card et vérifiez la syntaxe JSON

Problème : La confirmation n'apparaît pas

Solution : Assurez-vous que le décorateur @capabilities est correctement formaté avec l'objet confirmation

Problème : La propriété du modèle n'apparaît pas dans la réponse

Solution : Vérifiez si la propriété a besoin de @visibility(Lifecycle.Read) ou supprimez-la si elle doit être modifiable

Skills similaires