Retour au catalogue
Power PlatformPerso

power-apps-code-app

Use this skill whenever the user works on a Microsoft Power Apps code app project — deploying, pushing, rattaching to a solution, debugging connections, or preparing for ALM. Triggers include any mention of `pac code` commands, `pac code push`, `pac code add-data-source`, `npx power-apps push`, `@microsoft/power-apps`, `@microsoft/power-apps-cli`, `power.config.json`, "code app", "Power Apps Code App", pushing a code app to an environment or a solution, attaching a code app to a Dataverse solution, connection references in a code app, Copilot Studio agent in a code app, Office 365 / SharePoint / Dataverse connectors in a code app, or errors like `Cannot add CanvasApp ... to solution because it does not exist`. Do NOT use for classic canvas apps (.msapp), model-driven apps, Power Pages, or Power Automate flows — only for the newer "code apps" (React/Vite/TypeScript deployed via PAC CLI or npm CLI).

Power AppsDataversePAC
Chemin
~/.claude/skills/power-apps-code-app
Modifié
21 avril 2026 à 09:45

Power Apps Code Apps — déploiement et cycle de vie

Contexte et vocabulaire

Une Power Apps Code App est une app web (React/Vite/TypeScript typiquement) déployée dans un environnement Power Platform. Sous le capot elle est stockée dans Dataverse comme une entité canvasapp avec un flag "code app", ce qui explique plusieurs bizarreries héritées du monde canvas classique.

Trois packages npm entrent en jeu — ne pas confondre :

PackageRôleFournit le binaire power-apps
@microsoft/power-appsClient library (SDK runtime)Non
@microsoft/power-apps-cliCLI npm (power-apps command)Oui
@microsoft/power-apps-vitePlugin Vite de buildNon

Deux CLI coexistent, avec les mêmes capacités à peu près :

  • PAC CLI (pac code ...) — historique, .NET, à terme déprécié
  • npm CLI (npx power-apps ...) — le remplaçant recommandé par Microsoft

Les deux tapent la même API Dataverse en dessous, donc les mêmes bugs serveur existent des deux côtés.

Étape 1 — Premier déploiement d'une code app dans une solution

Ne fais pas confiance aux flags --solutionName / -s pour rattacher : ils sont parsés mais le rattachement Dataverse n'est pas effectué côté serveur (bug en preview confirmé). Procédure qui marche aujourd'hui :

1.1 Pré-requis

pac auth create --environment <ENVIRONMENT_ID>
pac env select --environment <ENVIRONMENT_ID>
pac env who   # confirme l'env actif

La solution cible doit déjà exister dans l'environnement et être unmanaged. Vérifier avec :

pac solution list

Noter le Unique Name (pas le Friendly Name) — c'est ce qu'attendent les CLI.

1.2 Vérifier les connexions nécessaires

Pour chaque connecteur qu'utilise l'app (Office 365 Users, SharePoint, Copilot Studio, Dataverse, etc.) :

pac connection list

Chaque connexion utile doit être en statut Connected. Si elle est en Error, la réparer d'abord (cf. section Troubleshooting connexions) — sinon pac code add-data-source renverra des 404 trompeurs sur une URL de connectivity (le 404 ne dit pas "connexion cassée", il dit "endpoint not found" alors que la vraie cause est la connexion en erreur).

1.3 Initialisation et ajout des data sources

pac code init --displayname "mon_app"
pac code add-data-source -a "shared_office365users" -c <connectionId>
# etc. pour chaque connecteur

1.4 Premier push

npm run build
npx power-apps push
# ou, équivalent: pac code push

Le push réussit et te donne une URL play. Note le GUID de l'app dans l'URL (.../app/<appId>?...).

1.5 Rattacher l'app à la solution (l'étape que les CLI ratent)

Voie portail (la plus fiable) :

  1. make.powerapps.com → environnement cible
  2. Solutions → ta solution
  3. + Ajouter existantAppApplication canevas
  4. Sélectionner l'app dans le picker → Ajouter

Le picker résout correctement l'App ID (utilisé côté PowerApps) vers le canvasappid (clé primaire Dataverse), qui sont différents. C'est pour ça que la voie CLI directe échoue :

# ÉCHOUE avec "Cannot add CanvasApp ... because it does not exist"
pac solution add-solution-component \
  --solutionUniqueName <solutionUniqueName> \
  --component <appId-from-url> \
  --componentType 300

Le --component attend le canvasappid Dataverse, que seule une requête OData (/api/data/v9.2/canvasapps?$filter=...) peut fournir — trop lourd pour un rattachement unique, d'où le portail.

Étape 2 — Pushes successifs (workflow quotidien)

Une fois l'app rattachée à la solution, la liaison persiste côté Dataverse. Plus besoin de flag ni de prompt :

npm run build
npx power-apps push

L'app est mise à jour dans la solution, version incrémentée, tout bon. Ne pas retenter --solutionName — ça ne casse rien mais ça n'apporte rien non plus.

Troubleshooting connexions

Symptôme : pac code add-data-source renvoie HTTP 404

Cause la plus fréquente : la connexion référencée est en statut Error dans pac connection list. Le 404 n'est pas descriptif.

Fix :

  1. make.powerapps.com → bon environnement → PlusConnexions
  2. Trouver la connexion avec l'icône d'avertissement
  3. Cliquer Corriger la connexion et ré-authentifier. Ou supprimer et recréer.
  4. pac connection list doit maintenant afficher Connected pour cette ligne.
  5. Relancer pac code add-data-source.

Symptôme : plusieurs connexions en Error simultanément

Très probablement des tokens OAuth expirés suite à changement de mot de passe / MFA / inactivité prolongée. Fix groupé via le portail (réparation une par une).

Symptôme : bon env mais pas les bonnes connexions

Vérifier qu'on est bien dans l'environnement attendu :

pac env who
pac env list
pac env select --environment <correctEnvId>

Bugs connus et workarounds (au 2026-04)

1. pac code push --solutionName / npx power-apps push -s parsés mais non effectifs

Symptôme : le push réussit, l'app est dans l'environnement, mais absente des composants de la solution ciblée. Workaround : rattachement manuel via portail (cf. Étape 1.5), une seule fois.

2. Prompts interactifs @clack/prompts invisibles sur macOS

Symptôme : npx power-apps push affiche uniquement des et des sans texte, puis pushe directement sans demander la solution. Terminal.app et terminal intégré VS Code touchés identiquement. Ne touche pas Windows. Workaround : passer les valeurs par flag plutôt qu'attendre le prompt — ou ignorer, puisque le rattachement est de toute façon à faire via portail.

3. --help de npx power-apps push incohérent

L'exemple affiche --solution-name mais le flag reconnu est -s / --solution-id (alors que ce dernier accepte aussi un nom unique, pas seulement un GUID). Utiliser -s <uniqueName>.

4. App ID (URL play) ≠ canvasappid (Dataverse PK)

Symptôme : pac solution add-solution-component --component <appId> renvoie Cannot add CanvasApp with id (…) to solution because it does not exist. Cause : l'App ID qu'on voit dans l'URL /play/e/<env>/app/<appId> n'est pas la clé primaire Dataverse. Workaround : utiliser le picker "Ajouter existant" du portail qui fait la résolution.

5. Versions des packages à bien distinguer

  • Le binaire power-apps vient de @microsoft/power-apps-cli (actuellement latest = 0.10.0).
  • Le SDK @microsoft/power-apps est sur latest = 1.1.1.
  • Mettre à jour le SDK n'update pas le CLI. Pour upgrader le CLI :
    npm install @microsoft/power-apps-cli@latest
    

6. Cache npx tenace

Si npx power-apps --version renvoie une vieille version malgré un upgrade :

rm -rf ~/.npm/_npx
npx --yes -p @microsoft/power-apps-cli@latest power-apps --version

Note la syntaxe -p <package> qui force le package source quand le nom de commande diffère du nom de package.

7. Code apps enabled sur l'environnement

Si un premier pac code push renvoie une 500 ou un message auth bizarre, vérifier dans Power Platform admin center → ton env → Settings → Product → Features qu'Enable code apps est activé.

ALM et connection references

Pour qu'une code app soit exportable/ré-importable proprement (CI/CD, dev→test→prod), les connexions ne doivent pas être en dur par connection ID — elles doivent passer par une connection reference stockée dans la solution.

Ajouter un connecteur avec connection reference

pac code add-data-source \
  -a "shared_microsoftcopilotstudio" \
  -cr <connectionReferenceLogicalName> \
  -s <solutionId>
  • -cr : nom logique de la connection reference (ex. pub_copilotStudioConn)
  • -s : GUID de la solution Dataverse

Créer la connection reference

Soit dans le portail (Solution → Nouveau → Plus → Connexion → Référence de connexion), soit via une solution unpacked. La connection reference pointe vers un connecteur (API ID) et sera résolue vers une vraie connexion au moment de l'import dans chaque environnement cible (via settings-file JSON).

Vérification

pac code list-connection-references --solutionId <solutionGuid>

Pour les data sources Dataverse

Pas besoin de connection reference — Dataverse est natif, l'app est liée par environmentId dans power.config.json. Ce sont les connecteurs tiers (Office 365, SharePoint, Copilot Studio, SQL, etc.) qui ont besoin de connection references pour être portables.

Types générés vs runtime réel — piège transverse

Règle d'or : les services TypeScript générés par PAC CLI dans src/generated/ peuvent mentir sur la forme du runtime. Plusieurs cas observés :

  • Type de retour annoncé IOperationResult<void> alors que result.data contient un objet structuré.
  • Signature générée en paramètres positionnels alors que MS Learn documente un objet ({ message, notificationUrl, agentName }).
  • Casing des propriétés qui varie selon le connecteur (conversationId vs ConversationId).
  • Propriétés non documentées (ex. isExpectingInput).

Workflow recommandé pour tout nouveau connecteur tiers :

  1. POC one-shot avant tout code de prod :
    // src/pages/poc-<connector>.tsx (dev-only route)
    const result = await SomeGeneratedService.SomeAction(...)
    return <pre>{JSON.stringify(result, null, 2)}</pre>
    
  2. Observer la forme réelle : shape de data, casing, champs non documentés, latence.
  3. Définir un type runtime dans src/types/ :
    export interface SomeActionResponse {
      fieldA?: string  // défensif — peut être absent
      FieldA?: string  // casing observé dans une variante
      unknownField?: boolean  // observé mais non documenté
    }
    
  4. Dans le hook, caster : result.data as unknown as SomeActionResponse.
  5. Extraction défensive avec optional chaining triple si le casing est incertain :
    const id = data?.conversationId ?? data?.ConversationId ?? data?.conversationID
    

Règle d'or associée : le SDK @microsoft/power-apps ne throw jamais. Il retourne { success: false, error }. Toujours checker result.success avant d'accéder à result.data — un try/catch autour d'un appel SDK ne masquera pas les échecs logiques.

Connecteur Copilot Studio dans une Code App

Pour brancher un agent Copilot Studio publié à une code app :

Setup

  1. Agent : créer l'agent dans Copilot Studio, publier, copier le nom exact depuis l'URL Channels → Web app (ex. gg_Agentdetest). Sensible à la casse, préfixe éditeur inclus.
  2. Connexion : dans le portail Power Apps, créer/vérifier une connexion shared_microsoftcopilotstudio en statut Connected. Si Error, réparer avant tout add-data-source.
  3. Env var : stocker le nom de l'agent dans .env.local (ignoré via *.local) :
    VITE_COPILOT_AGENT_NAME=<publisher_agentName>
    
  4. Data source :
    npx power-apps add-data-source -a "shared_microsoftcopilotstudio" -c <connectionId>
    # ou pac code add-data-source ...
    

Pièges spécifiques Copilot Studio

PiègeRéalitéMitigation
Trois actions disponiblesExecuteCopilot (fire-and-forget, pas de réponse) et ExecuteCopilotAsync (peut 502)Utiliser ExecuteCopilotAsyncV2 uniquement
Signature doc MS LearnObjet { message, notificationUrl, agentName }La signature générée est positionnelle : (Copilot, body, x_ms_conversation_id?, environmentId?)
Type de retour généréIOperationResult<void>Runtime réel : { responses: string[], conversationId: string, completed: boolean, isExpectingInput: boolean }
Nom de la classe généréePas forcément CopilotStudioServiceObservé : MicrosoftCopilotStudioService
notificationUrl requisChamp string obligatoire même en mode syncValeur placeholder : "https://notificationurlplaceholder"
Casing conversationIdParfois ConversationId, parfois conversationIDExtraction triple optional chaining
Pas de streamingLatence ~1-5s visibleTyping indicator pendant status === "sending"
Payload utilisateurUn seul champ string (message)JSON.stringify un objet { userMessage, context, conversation } et parser côté agent

Pattern d'appel minimal

const result = await MicrosoftCopilotStudioService.ExecuteCopilotAsyncV2(
  import.meta.env.VITE_COPILOT_AGENT_NAME,
  { message: JSON.stringify(payload), notificationUrl: "https://notificationurlplaceholder" },
  previousConversationId ?? undefined,
)
if (!result.success) throw result.error
const data = result.data as unknown as CopilotRuntimeResponse
const convId = data?.conversationId ?? data?.ConversationId ?? data?.conversationID
const reply = data?.responses?.[data.responses.length - 1] ?? data?.lastResponse ?? ""

Contextualisation par injection JSON

Pour que l'agent contextualise ses réponses avec des filtres de l'app (ex. entreprise sélectionnée), wrapper le message utilisateur dans un payload JSON avec un champ context, et faire parser ce JSON côté topic de l'agent (action "Parse JSON" sur la variable Activity.Text). Exposer ce payload à l'utilisateur via un panneau repliable "Contexte envoyé à l'agent" pour la transparence.

Anti-patterns à signaler

  • ❌ Éditer les fichiers sous src/generated/ : réécrits à chaque pac code add-data-source.
  • ❌ Committer power.config.json avec un environmentId de production partagé : préférer plusieurs fichiers de config ou des variables d'env.
  • ❌ Pousser des connexions avec des IDs en dur si l'app doit être transportable → utiliser connection references.
  • ❌ Relancer 10 fois pac code push --solutionName en espérant que le rattachement finisse par fonctionner : il ne fonctionnera pas tant que le bug serveur n'est pas résolu.
  • ❌ Faire confiance au type de retour IOperationResult<void> d'un service généré sans POC préalable : plusieurs actions renvoient en réalité des objets structurés.
  • ❌ Utiliser try/catch autour d'un appel SDK Power Apps en pensant gérer les erreurs : le SDK retourne { success: false, error }, il ne throw pas. Toujours checker result.success.
  • ❌ Utiliser l'action ExecuteCopilot (fire-and-forget) en espérant une réponse : elle ne retourne rien. Seul ExecuteCopilotAsyncV2 renvoie le contenu.

Références