Power Apps Code Apps

A complete foundation for building enterprise-grade Code Apps with React, Fluent UI v9, and Power Platform. Use this template, run the wizard, and follow the intended sequence: plan first, prototype second, connect later.

πŸ—ΊοΈ Template to Production β€” in 10 Steps

From clicking Use this template on GitHub to promoting through Power Platform Pipelines, every Foundations team follows the same proven path. One picture says it all.

Power Apps Code Apps Foundations β€” From Template to Production: a 10-step journey showing Use the Template, Run the Wizard, Sample App Live, Plan Mode in VS Code, Agent Mode Builds the POC, Connect Connectors, Bind to Dataverse, Test, Share & Collaborate, and Promote with Pipelines.

The rest of this guide explains how β€” section by section.

Start Here

The README and the Guide describe the same workflow. Pick the format that matches how you want to onboard.

πŸ“

Start in the README

Use the concise text version if you want the shortest path through cloning, running the wizard, and understanding the repo structure.

Open README.md

🧭

Stay in the Guide

Use this page if you want the same procedure with richer context, step breakdowns, portal links, and operational cautions in one place.

Jump to the setup wizard

πŸ”

Need the Full Delivery Path?

Use the prototype-first golden path when you want the end-to-end sequence from business planning through schema hardening and real providers.

Open the prototype-first golden path

What's Inside

The repo contains instruction files, helper scripts, and a setup wizard that work together.

PAppsCAFoundations/ β”œβ”€β”€ .github/ β”‚ └── instructions/ # Canonical coding-agent instructions β”‚ β”œβ”€β”€ 00-before-you-start.instructions.md # Publisher, environments, solution β”‚ β”œβ”€β”€ 00-environment-setup.instructions.md # App Registration, auth, 1Password β”‚ β”œβ”€β”€ 00a-business-problem-decomposition.instructions.md # Turn freeform business narratives into planning inputs β”‚ β”œβ”€β”€ 00b-scope-refinement-and-solution-shaping.instructions.md # Refine workflows, automation, Teams, reporting, governance β”‚ β”œβ”€β”€ 00c-solution-concept-to-dataverse-plan.instructions.md # Bridge refined scope into Dataverse planning β”‚ β”œβ”€β”€ 00d-prototype-validation.instructions.md # Validate UX with mock data before schema hardens β”‚ β”œβ”€β”€ 01-scaffold.instructions.md # Project structure, tech stack β”‚ β”œβ”€β”€ 02-connectors.instructions.md # Dataverse, SQL, O365, Custom APIs β”‚ β”œβ”€β”€ 03-components.instructions.md # React + Fluent UI v9 patterns β”‚ β”œβ”€β”€ 04-deployment.instructions.md # CI/CD, pac code push, ALM β”‚ β”œβ”€β”€ 05-testing.instructions.md # Vitest, Playwright, MSW β”‚ β”œβ”€β”€ 06-security.instructions.md # Auth, secrets, DLP, validation β”‚ β”œβ”€β”€ 07-dataverse-schema.instructions.md # Tables, columns, option sets β”‚ └── 08-copilot-studio.instructions.md # Copilot Studio agent integration β”œβ”€β”€ docs/ β”‚ β”œβ”€β”€ index.html # Marketing landing page (high-level overview) β”‚ β”œβ”€β”€ guide.html # Interactive visual setup guide (this page) β”‚ └── prototype-golden-path.md # End-to-end delivery from planning to real providers β”œβ”€β”€ scripts/ β”‚ β”œβ”€β”€ decrypt-secret.mjs # Decrypt AES-256-GCM secrets from .env.local β”‚ β”œβ”€β”€ discover-copilot-connection.mjs # Cross-platform Copilot Studio connection discovery β”‚ β”œβ”€β”€ discover-copilot-connection.sh # Resolve Copilot Studio connection IDs β”‚ β”œβ”€β”€ export-solution.mjs # Export unmanaged, refresh solution-source, pack managed β”‚ β”œβ”€β”€ generate-dataverse-plan.mjs # Expand planning payloads into execution plans β”‚ β”œβ”€β”€ op-pac.mjs # Cross-platform 1Password wrapper for pac β”‚ β”œβ”€β”€ op-pac.sh # Legacy Bash wrapper for pac β”‚ β”œβ”€β”€ pac-safe.mjs # Safe pac wrapper with version and profile guards β”‚ β”œβ”€β”€ patch-datasources-info.mjs # Patch datasources-info.json after changes β”‚ β”œβ”€β”€ pre-commit-hook.sh # Block accidental secret commits β”‚ β”œβ”€β”€ register-dataverse-data-sources.mjs # Register Dataverse tables via PAC β”‚ β”œβ”€β”€ register-dataverse-data-sources.sh # Bash wrapper for table registration β”‚ β”œβ”€β”€ schema-plan.example.json # Starter Dataverse planning artifact β”‚ β”œβ”€β”€ seed-prototype-assets.mjs # Generate contracts, mock providers, feedback artifacts β”‚ β”œβ”€β”€ setup-auth.mjs # Cross-platform auth setup (1Password or .env.local) β”‚ β”œβ”€β”€ setup-auth.sh # Legacy Bash auth setup β”‚ β”œβ”€β”€ setup-wizard.sh # Bash wrapper to the Node wizard β”‚ β”œβ”€β”€ sync-foundations.mjs # Cross-platform template sync entry point β”‚ β”œβ”€β”€ sync-foundations.sh # Pull latest from template repo β”‚ β”œβ”€β”€ validate-schema-plan.mjs # Validate planning payloads before provisioning β”‚ └── tests/ # Unit tests for scripts β”œβ”€β”€ wizard/ # ← THE MAIN EVENT β”‚ β”œβ”€β”€ index.mjs # Cross-platform Node.js setup wizard β”‚ β”œβ”€β”€ lib/ # Shared helpers (state, UI, shell) β”‚ └── steps/ # 9 step modules β”œβ”€β”€ solution/ # Power Platform solution artifacts β”œβ”€β”€ .env # 1Password references (safe to commit) β”œβ”€β”€ .env.template # Template for non-1Password teams β”œβ”€β”€ .foundations-version.json # Bundle/version metadata for downstream syncs β”œβ”€β”€ .gitignore └── README.md
πŸ“‹

14 Instruction Files β€” 4 Agents

Native guidance for GitHub Copilot, Claude Code, Cursor, and Codex. Each agent reads the same rules in its own format. The canonical source lives in .github/instructions/; Claude, Cursor, and Codex projections are generated from it.

🧠

Narrative-First Planning

Optional, but high leverage. If the app idea is still taking shape, this planning layer helps your coding agent decompose the business narrative, challenge gaps, and refine scope before schema and connectors harden.

πŸ§™

Setup Wizard

One script, 9 steps, zero guesswork. Creates publisher and solution via API, sets up auth, scaffolds your app, adds a dedicated connector-binding pass, and deploys from the terminal.

πŸ”

Auth Scripts

1Password integration or .env.local fallback. Client secrets are encrypted at rest with AES-256-GCM. Service Principal auth means no browser popups β€” locally or in CI/CD.

πŸ”„

Sync Foundations

Pull the latest instruction files, wizard updates, and security patches from the template repo with npm run sync:foundations. Your project code is never touched.

Before scaffolding? If you haven't run Step 7 yet, there's no root package.json and npm run won't work. Use bash scripts/sync-foundations.sh or node scripts/sync-foundations.mjs directly instead.
πŸ”—
How agent guidance stays in sync

Every coding agent needs its instructions in a different format, but the underlying rules must be identical. Foundations solves this with a single canonical source β†’ multiple generated projections model:

  1. Canonical source β€” .github/instructions/*.instructions.md (Copilot-native format) plus the hand-authored root AGENTS.md.
  2. Generated projections β€” A manifest (agent-guidance.config.json) maps each canonical file to its Claude (.claude/rules/), Cursor (.cursor/rules/), and Codex (nested AGENTS.md) equivalents.
  3. Drift prevention β€” npm run guidance:check fails if any projected file is missing or unmarked. npm run guidance:generate regenerates all projections.

The rule: edit the canonical .github/instructions/ files, then run npm run guidance:generate. Never edit projected files directly β€” they carry a "do not edit" marker and will be overwritten.

Required Tech Stack

Every Code App uses this exact stack. No substitutions without team lead approval.

LayerChoiceVersionWhy
LanguageTypeScript5.xType safety across codebase + connector models
UI FrameworkReact18.xMicrosoft-recommended; all official samples target React
Design SystemFluent UI v9@fluentui/react-componentsNative Microsoft look; matches Power Platform chrome
BundlerVite5.xFast HMR; official templates use Vite
Server StateTanStack Queryv5Caching, deduplication, background refresh for connectors
RoutingReact Routerv6Nested layouts, data loaders
Power Apps SDK@microsoft/power-apps^1.0.3Connector access, auth context
CLIPAC CLIlatest (not 2.3.2)Scaffold, add data sources, deploy
EditorVS Code / CursorLatestOr any editor with coding-agent support
Coding AgentCopilot / Claude / Cursor / Codexβ€”Any LLM-backed agent reads the repo's native guidance
TypeScript 5 React 18 Fluent UI v9 Vite 5 TanStack Query v5 React Router v6 Vitest Playwright MSW

Narrative-First Planning Layer

Use this when the business problem is still being worked out and you want the eventual app to be more than a fast scaffold.

🧠
This is optional, but it is part of the formula for a top-notch app. If the user already knows exactly what they are building, the wizard can get them moving quickly. If the problem, workflows, approvals, reporting, Teams touchpoints, or Microsoft Copilot Studio usage are still emerging, the planning layer helps your coding agent refine the scope before schema and connectors become expensive to change. The new prototype checkpoint sits between conceptual planning and Dataverse provisioning so the UX can influence the final data model.
1

Decompose the Narrative

Your coding agent starts from the user's freeform description, identifies goals, actors, workflows, outputs, constraints, and unknowns, then asks only the most useful follow-up questions.

00a-business-problem-decomposition.instructions.md

2

Refine the Scope

Your agent challenges for exception paths, validations, approvals, automation, Teams, Microsoft 365 outputs, reporting, governance, and Microsoft Copilot Studio placement decisions.

00b-scope-refinement-and-solution-shaping.instructions.md

3

Convert to Technical Planning

Once the scope is stable, your agent derives candidate entities, relationships, ownership patterns, and lifecycle states, then hands off into the Dataverse planning artifact workflow.

00c-solution-concept-to-dataverse-plan.instructions.md

4

Prototype Before Schema

Generate domain contracts and mock providers from the planning payload, build the UX in prototype mode, and capture what that prototype changes in the eventual schema.

00d-prototype-validation.instructions.md

βœ…
Use the wizard to get operational fast. Use the planning layer to get the business fit right. Together, they help teams avoid premature schema decisions, guessed connectors, weak approval models, and late-stage rework.
⚠️
Expected methodology: the initial wizard run scaffolds the app and seeds prototype assets. It does not assume you already know the connector mix or the correct connection IDs. Real connector binding is a later step after the prototype has influenced the planning payload.

⚠️ Enable Code Apps in Your Environment

This is a prerequisite. Without it, pac code push will fail with "does not allow this operation for this Code app."

🚨
You MUST do this before your first push. Code Apps are disabled by default in Power Platform environments. If you skip this step, the first pac code push will fail even with correct auth.

Steps

  1. Open β†— admin.powerplatform.microsoft.com
  2. Select your development environment
  3. Go to Settings β†’ Features
  4. Find "Code components for canvas apps" β†’ toggle ON
  5. Find "Allow publishing of canvas apps with code components" β†’ toggle ON
  6. Click Save
  7. Wait ~1 minute for the setting to propagate
ℹ️
Repeat for each environment (Dev, Test, Prod) where you want to deploy Code Apps. You only need to do this once per environment β€” the setting persists.

Auth for pac code push

pac code push requires a user auth profile β€” the BAP checkAccess API rejects SPN tokens. This is a one-time setup: create a user profile via device-code sign-in, and all subsequent pushes work silently. PAC CLI caches a refresh token that auto-renews (~90 days).

For CI/CD to test/production, use solution export/import instead of pac code push. Solution export and import both accept SPN auth and deploy the Code App, tables, connection references, and security roles as one atomic unit. See the deployment instructions for the full pipeline.

The Setup Wizard

This is the same quick-start flow described in the README. For non-trivial apps, pair it with the planning layer and prototype checkpoint before locking in schema and connectors.

βœ…
Step 1 β€” Get the code (pick one):
  • Recommended: Click "Use this template" on GitHub β†’ create your repo β†’ git clone https://github.com/your-org/my-app.git
  • Quick explore: npx degit martycarreras-psnl/PAppsCAFoundations my-app
Step 2 β€” Run the wizard β€” pick the experience you prefer:

Terminal wizard (the original):

cd my-app/wizard && npm install && node index.mjs

✨ Browser wizard (new) β€” same flow, beautiful Fluent UI experience at http://127.0.0.1:5174:

cd my-app && npm run wizard:ux

Both wizards share .wizard-state.json, so you can switch between them at any time without losing progress. Steps that need interactive auth or long-running CLI sessions show a one-click "copy command" handoff in the browser version.

Works on Windows, macOS, and Linux.
The wizard saves your progress β€” quit with Ctrl+C and pick up where you left off. Reset with --reset.

Want a stronger business fit? After the wizard gives you a working foundation, use the narrative-first planning instructions and prototype checkpoint before finalizing Dataverse schema, connectors, approvals, or agent behavior.

⚠ Do not git clone the template repo directly β€” that leaves origin pointing to PAppsCAFoundations instead of your own repo.

Browser wizard home page showing the nine-step grid: Prerequisites, Project & Environment, App Registration, PAC Auth Profiles, Publisher, Solution, Scaffold the Code App, Bind Connectors, and Verify & Deploy.
The browser wizard at http://127.0.0.1:5174 β€” nine self-contained, re-runnable steps. Smart paste means you can drop a Maker Portal connection URL straight in and the wizard pulls the connector apiId and the connection GUID out for you.
πŸ”

Steps 1–2

Check tools, name project & environments

πŸ”

Steps 3–4

App Registration & auth setup

🌐

Steps 5–6

Publisher & solution (auto via API)

πŸš€

Steps 7–9

Scaffold, bind connectors, build & deploy

All 9 Wizard Steps

Expand each step to see what happens and what you'll need.

The wizard checks for required tools and offers to install missing ones:

  • Editor β€” VS Code (Install), Cursor, or any editor with coding-agent support
  • Coding Agent β€” GitHub Copilot, Claude Code, Cursor, Codex, or another LLM-backed agent (see below)
  • Node.js 20+ β€” Install
  • Git β€” Install
  • .NET SDK β€” Install
  • PAC CLI β€” The wizard will install it automatically via dotnet tool install -g Microsoft.PowerApps.CLI.Tool if missing
  • 1Password CLI (optional) β€” Install
πŸ’‘
Tip: Once your editor and coding agent are set up, you can ask the agent to install the remaining prerequisites (Node.js, Git, .NET SDK, PAC CLI) for you β€” it can run the install commands directly in the integrated terminal.
πŸ€–
Coding agent options: GitHub Copilot (uses OpenAI or Azure OpenAI models), Claude Code (Anthropic's Claude via direct API or Azure AI Foundry), Cursor (built-in agent), Codex (OpenAI via ChatGPT subscription or API), or any agent backed by Azure OpenAI, Azure AI Foundry, or a direct paid LLM API. The repo ships native guidance for all of them.
⚠️
PAC CLI version 2.3.2 has a known bug that breaks pac code push. The wizard detects this and helps you downgrade.

The wizard prompts you for your app name and environment URLs:

ValueExampleNotes
App nameMy BrainDisplay name, any characters
Dev environment URLhttps://org-dev.crm.dynamics.comRequired. Must have Dataverse enabled.
Test environment URLhttps://org-test.crm.dynamics.comOptional β€” press Enter to skip
Prod environment URLhttps://org.crm.dynamics.comOptional β€” press Enter to skip

If you don't have a development environment yet, create one first:

  1. Open β†— admin.powerplatform.microsoft.com β†’ Environments β†’ + New
  2. Name it Your App - Dev, type Developer or Sandbox
  3. Toggle "Add Dataverse" to YES (required!)
  4. Copy the Environment URL after provisioning
🚨
Don't forget to enable Code Apps! In the Admin Center β†’ your environment β†’ Settings β†’ Features β†’ turn on "Code components for canvas apps" and "Allow publishing of canvas apps with code components".

This step collects your Azure App Registration credentials. If you use 1Password, the wizard detects it and tries to pull credentials automatically.

1Password Users (fastest path)

If the op CLI is detected, the wizard asks for your vault and item name, then reads tenant-id, app-id, and client-secret directly from 1Password. If all three are found, you skip all manual entry.

βœ…
Expected 1Password item fields:
  • tenant-id (Text) β€” your Azure Directory ID
  • app-id (Text) β€” your App Registration Client ID
  • client-secret (Password) β€” your App Registration secret
  • env-dev (Text) β€” your Dev environment URL
  • env-test (Text, optional) β€” Test environment URL
  • env-prod (Text, optional) β€” Prod environment URL

If the item doesn't exist yet, the wizard will collect values manually and offer to create the 1Password item for you.

Manual Path (no 1Password, or partial credentials)

If 1Password isn't available, or some fields are missing, the wizard guides you through creating the App Registration:

A. Create Azure App Registration

  1. Open β†— Azure Portal β€” App registrations
  2. Click + New registration
  3. Name: PowerApps-CodeApps-YourApp, Single tenant
  4. Copy Application (client) ID and Directory (tenant) ID
  5. Go to Certificates & secrets β†’ + New client secret
  6. Copy the secret value immediately β€” shown only once!
  7. Go to API permissions β†’ + Add β†’ Dataverse β†’ user_impersonation
  8. Click Grant admin consent

B. Register as Application User

  1. Open β†— admin.powerplatform.microsoft.com
  2. Select environment β†’ Settings β†’ Users + permissions β†’ Application users
  3. + New app user β†’ search for your App Registration β†’ assign System Administrator role
ℹ️
The secret is held in memory only β€” never written to disk in plain text. If you entered values manually and use 1Password, the wizard offers to save them to your vault before proceeding.

The wizard takes the credentials from Step 3 and handles everything:

  • If you chose 1Password in Step 3, the storage mode and vault/item are already set β€” no repeated questions
  • Otherwise, choose 1Password or .env.local for secret storage
  • Writes credential files (op:// references or encrypted values)
  • Ensures .env.local is in .gitignore
  • Creates repo-scoped PAC auth profiles for each environment instead of machine-global Dev/Test/Prod names
  • Runs pac org who to verify the selected profile matches the wizard target environment and should show your App Registration name, not a human user
  • Installs a pre-commit hook to prevent accidental secret leaks
βœ…
After this step, all pac commands work without any browser popup. SPN auth is used for Dataverse API calls, publisher/solution creation, and CI/CD (solution export/import). pac code commands (push, add-data-source) need a user profile β€” created once via the wizard (one-time sign-in, then silent). The wizard handles this automatically in the later connected-mode steps.

The wizard queries Dataverse directly to find existing publishers or create a new one β€” no Maker Portal visit needed.

  • Lists all existing publishers in the environment via Dataverse Web API
  • Lets you select an existing publisher or create a new one
  • If creating new: prompts for prefix, display name, and internal name
  • Creates the publisher via API with all required properties
  • Records the Choice value prefix automatically
ValueExampleRules
Publisher prefixmybrn2–8 lowercase letters only. Cannot change later.
Publisher display nameMy Brain EngineeringHuman-readable
Publisher internal namemybrainengineeringLowercase, no spaces
🚨
The publisher prefix is the most consequential naming decision. It prefixes every Dataverse table, column, option set, environment variable, and security role. It cannot be changed after data exists.

Like the publisher step, the wizard creates or selects a solution via Dataverse Web API β€” no Maker Portal visit needed.

  • Lists all existing unmanaged solutions in the environment
  • Lets you select an existing solution or create a new one
  • If creating new: prompts for display name and unique name
  • Creates the solution via API, bound to your publisher from Step 5
ValueExampleNotes
Solution display nameMy BrainShown in the Maker Portal solution list
Solution unique nameMyBrainNo spaces. Used in CLI commands and API calls.

The wizard creates a complete, ready-to-develop project:

  • Downloads the official starter template (or creates a minimal fallback)
  • Installs all dependencies (React, Fluent UI v9, TanStack Query, @microsoft/power-apps)
  • Writes tsconfig.json with moduleResolution: "bundler"
  • Writes vite.config.ts with port 3000, @/ path alias, and relative base path for deploy
  • Creates the full folder structure (components, pages, hooks, utils, tests, etc.)
  • Writes starter files: main.tsx, App.tsx with Fluent UI + TanStack Query providers
  • Writes vitest.config.ts with jsdom, jest-dom matchers, @/ alias, and coverage thresholds
  • Creates smoke test infrastructure: tests/setup/setup.ts, tests/setup/test-utils.tsx (custom render with all providers), and src/App.test.tsx
  • Runs npm run test:smoke automatically to verify the scaffold is healthy before proceeding
  • Copies all instruction files and agent-native guidance (.claude/rules/, .cursor/rules/, nested AGENTS.md) from the foundations repo
  • Includes the narrative-first planning instructions so you can refine the business scope before hardening schema and connector choices
  • Seeds prototype contracts, mock providers, and dataverse/prototype-feedback.md from the planning payload
  • Runs pac code init to register the app in Power Platform
  • Defers connector setup by default so the team can prototype before binding real systems
  • Initializes Git repo, detects or prompts for remote URL, makes initial commit
  • Generates a project-specific README (replaces the template README)
βœ…
Smoke tests pass from day one. Every scaffold includes vitest.config.ts, a test setup with provider wrappers, and src/App.test.tsx with three smoke tests. The wizard runs npm run test:smoke automatically during this step β€” if they pass, your scaffold is verified healthy. You can re-run anytime with npm run test:smoke or npm run test.

Connector & Data Source Setup

The initial scaffold no longer asks for connector IDs up front. The intended flow is to return later once the planning payload and prototype are stable enough to bind real systems.

# Terminal wizard:
node wizard/index.mjs --from 8

# Browser wizard β€” navigate to Step 8 (Bind Connectors):
npm run wizard:ux
🚨
pac code commands need a user auth profile (not SPN). The wizard detects SPN auth and automatically switches to a user profile before running pac code add-data-source. This user profile only needs to be created once β€” subsequent calls work silently.

When you do re-run connector setup, the wizard first tries pac connection list, filters to the selected connector API ID, and lets you choose from discovered connections. Manual Connection ID entry is only the fallback.

How to Get Connection IDs

You must create connections before adding them as data sources. See the Creating Connections section below for step-by-step instructions. Once they exist, the later wizard connector flow can often discover them for you.

⚠️
If your app concept is still evolving, do not treat Step 7 as permission to guess at tables, workflows, or connectors. Use prototype mode and the feedback artifact first, then bind connectors after the planning payload is stable.

This is the first connected-mode step. Run it only after the planning payload and prototype are stable enough that connector choices are no longer guesses.

  1. Presents a checklist of the seven most common connectors (Office 365 Users, SharePoint, Dataverse, SQL, Teams, Blob, Outlook)
  2. Then loops on "Add another connector by URL or apiId (blank to finish)" β€” paste any Maker Portal connection URL or a bare shared_xxx apiId, including custom connectors and connectors not on the shortlist
  3. Creates solution-level connection references for the connectors you actually chose
  4. Runs pac connection list and filters matches to the selected connector API ID
  5. Lets you select an existing connection when matches are found
  6. Falls back to manual Connection ID entry only when discovery does not find a usable match
  7. Adds the selected data sources to the Code App so generated services can be produced later
✨
Smart paste β€” never hand-edit a URL again. Paste a Maker Portal connection-details URL like .../connections/shared_office365users/<GUID>/details and the wizard extracts both the connector apiId and the connection GUID in one shot β€” no separate Connection-ID prompt for that connector. Pasting just a bare GUID still works, and bare apiIds (e.g. shared_approvals) skip the URL entirely and go straight to connection-ID resolution. Environment URLs are similarly tolerant β€” paste straight from PPAC, with or without https://.
⚠️
This step uses a user auth profile for pac code add-data-source. The wizard switches from SPN automatically when needed. If the profile already exists, no sign-in is required.

The wizard runs:

  1. npm run build β€” verifies dist/index.html exists
  2. Checks power.config.json for appId to determine first vs. subsequent push
  3. First push: Creates a user auth profile via browser sign-in (one-time)
  4. Subsequent pushes: Work silently β€” the cached refresh token auto-renews (~90 days)
  5. Offers to deploy through the verified PAC flow instead of trusting the active machine profile
⚠️
Make sure you've enabled Code Apps in the environment before your first push (Settings β†’ Features β†’ "Code components for canvas apps"), or it will fail.

Then displays a complete summary of everything that was set up and what to do next:

cd ../your-app
npm run dev:local    ← prototype with mock providers
npm run prototype:seed ← refresh prototype assets after editing the planning payload
npm run dev          ← connected mode once real providers exist
node wizard/index.mjs --from 8 ← bind real connectors when ready
npm run deploy       ← deploy changes after the app has been registered

Creating Connections

Before moving from prototype mode into connected mode, create the actual connection instances in the Maker Portal.

A connection reference (created by the wizard in your solution) is a pointer that says "this app uses the Office 365 Users connector." The connection is the authenticated instance β€” the actual credential that makes it work. You need both.

Step-by-Step: Create a Connection

  1. Go to β†— make.powerapps.com and select your development environment
  2. In the left nav, click Connections (under Data, or directly at make.powerapps.com/environments/<env-id>/connections)
  3. Click + New connection
  4. Search for the connector (e.g. Office 365 Users) and click it
  5. Click Create and authenticate when prompted
  6. Once created, click the connection to open its details
  7. Copy the Connection ID from the browser URL β€” it's the GUID at the end:
    https://make.powerapps.com/environments/xxx/connections/shared_office365users/160ffdfd-aff5-4740-86a8-47209c1c608c/details

Common Connectors

ConnectorAPI ID (for pac CLI)
Dataversedataverse (uses -t <table> instead of -c)
Office 365 Usersshared_office365users
SharePointshared_sharepointonline
Office 365 Outlookshared_office365
Microsoft Teamsshared_teams
SQL Servershared_sql
Azure Blob Storageshared_azureblob

Adding Data Sources (After Getting Connection IDs)

Preferred path: rerun the wizard when you are ready to connect real data.

# Terminal wizard:
node wizard/index.mjs --from 8

# Or use the browser wizard β€” navigate to Step 8:
npm run wizard:ux

The wizard will create the solution-level connection references, try to discover existing environment connections with pac connection list, and let you select one when matches are found.

# Dataverse tables (no connection ID needed):
pac code add-data-source -a dataverse -t myprefix_project

# Non-Dataverse connectors (connection ID required):
pac code add-data-source -a shared_office365users -c 160ffdfd-aff5-4740-86a8-47209c1c608c
pac code add-data-source -a shared_sharepointonline -c <YOUR_CONNECTION_ID>
🚨
Requires a user auth profile (SPN is not supported for pac code commands). Create one once β€” it works silently after that:
pac auth create --name pp-myrepo-d-u-1a2b3c4d --environment https://org-dev.crm.dynamics.com --deviceCode
pac auth select --name pp-myrepo-d-u-1a2b3c4d
ℹ️
Connections are environment-specific. You need a separate connection (and Connection ID) for dev, test, and prod. Create them in each environment before deploying your solution there.

Local Development with Vite

Two modes: Prototype (mock providers, no Power Platform) and Connected (real connectors). Always start with Prototype mode.

⚑

Prototype Mode

Vite only. Domain contracts plus mock providers. No auth, no connections, no Power Platform needed. Build UI fast and let the UX challenge the data model.

npm run dev:local
πŸ”—

Connected Mode

Vite + pac code run. Real connector calls through the Power Platform proxy.

npm run dev

When to Use Each Mode

PhaseCommandWhat RunsData Source
Prototyping / UInpm run dev:localVite onlyMock data
Refresh prototype assetsnpm run prototype:seedNode scriptPlanning payload
Integrationnpm run devVite + pac code runReal connectors
Unit Testsnpm run testVitestMock data (MSW)
E2E Testsnpm run test:e2eVite + PlaywrightMock data
Production Buildnpm run buildtsc + Vite buildN/A (static)
Deploynpm run deployBuild + pac code pushN/A

Prototype Mode β€” How It Works

npm run dev:local runs VITE_USE_MOCK=true vite --port 3000. Vite serves your app at http://localhost:3000 with sub-second HMR β€” save a file, see it instantly. Refresh prototype contracts and mock providers anytime with npm run prototype:seed after editing dataverse/planning-payload.json.

βœ…
What works: All React components, routing, Fluent UI, TanStack Query hooks (resolving mock promises), form validation, state management, error boundaries, Playwright E2E tests.
⚠️
What doesn't: Real connector calls, PowerProvider context, auth context, connection consent β€” these require pac code run.

Need the full sequence from planning payload through prototype review to real providers? Open the prototype-first golden path.

Mock Data Pattern

Every hook that calls a connector checks VITE_USE_MOCK to decide whether to return mock data or make a real call:

// src/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { mockProjects } from '@/mockData/projects';
import { ProjectService } from '@/generated/services/ProjectService';

const USE_MOCK = import.meta.env.DEV && import.meta.env.VITE_USE_MOCK === 'true';

export function useProjects() {
  return useQuery({
    queryKey: ['projects'],
    queryFn: USE_MOCK
      ? () => Promise.resolve({ data: mockProjects })
      : () => ProjectService.getAll(),
  });
}

Connected Mode β€” Prerequisites

  • PAC auth profiles created (pac org who shows org, no popup)
  • Data source added: pac code add-data-source
  • At least one data source added with pac code add-data-source so src/generated/ exists
  • power.config.json exists next to package.json

Vite Configuration

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig(({ command }) => ({
  base: command === 'build' ? './' : '/', // CRITICAL for Code Apps
  plugins: [react()],
  server: {
    port: 3000,          // Required by Power Apps SDK
    strictPort: true,    // Fail if 3000 occupied (don't silently use 3001)
    open: true,          // Auto-open browser
  },
  resolve: {
    alias: { '@': path.resolve(__dirname, './src') },
  },
  build: { outDir: 'dist' },
}));

base: './' is required for production builds. Power Apps serves your bundle from a nested, non-root path. Without relative base, all asset URLs are absolute (/assets/index-abc.js) which 404 β†’ blank screen.

Vite Environment Variables

Only variables prefixed with VITE_ are exposed to client code via import.meta.env. This is a security feature β€” Power Platform credentials (PP_*) are never leaked to the browser.

VariablePurposeSet In
VITE_USE_MOCKToggle mock datadev:local script or .env.development
VITE_APP_TITLEApp display name (optional).env or .env.development

Troubleshooting

ProblemFix
Port 3000 already in uselsof -ti:3000 | xargs kill -9
pac code run failsCheck pac org who, avoid PAC v2.3.2, confirm power.config.json
HMR not refreshingVerify react() plugin in vite.config.ts, clear cache: rm -rf node_modules/.vite
Mock data shape mismatchRe-run pac code add-data-source for the affected table or connector, then compare with src/generated/models/

See .github/instructions/01-scaffold.instructions.md for complete Vite config, mock data patterns, and mutation mocking.

Connectors

Code Apps cannot make direct HTTP calls. All data access goes through Power Platform connectors, but connector binding should happen after prototype validation.

🚨
No fetch(). No axios(). No direct HTTP. Code Apps run in a sandbox with connect-src 'none'. If a connector exists for the service, use it β€” no exceptions.

Adding a Data Source

Recommended order: prototype first, then rerun the wizard from the dedicated connector step once the planning payload is stable.

# Terminal wizard:
node wizard/index.mjs --from 8

# Or use the browser wizard β€” navigate to Step 8:
npm run wizard:ux
# Dataverse (no connection ID needed):
pac code add-data-source -a dataverse -t myprefix_project

# Non-Dataverse connectors (connection ID required β€” see Creating Connections):
pac code add-data-source -a shared_office365users -c <CONNECTION_ID>
🚨
pac code commands need a user auth profile (not SPN). Create the profile once, then all subsequent calls work silently. Switch to your repo-scoped user profile for the current environment before running them.

Wrapping Generated Code with TanStack Query

// src/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { SqlService } from '@/generated/services/SqlService';

export function useProjects() {
  return useQuery({
    queryKey: ['projects'],
    queryFn: () => SqlService.getProjects(),
    staleTime: 5 * 60 * 1000,  // Connector calls aren't free
  });
}
⚠️
Never edit files in src/generated/. They're auto-generated by the PAC CLI. If you need to extend a type, create a wrapper in src/types/.

See .github/instructions/02-connectors.instructions.md for full patterns including Dataverse OData queries, pagination, and error handling.

Components

React + Fluent UI v9 patterns for consistent, accessible UI.

Key Principles

  • Functional components only (no class components)
  • Single responsibility β€” files under ~150 lines
  • Fluent UI v9 (@fluentui/react-components) for all controls
  • Style with makeStyles + design tokens β€” never raw CSS values
  • Local state (useState) for UI; Server state (TanStack Query) for data
  • Colocate tests: ProjectCard/ProjectCard.tsx + ProjectCard.test.tsx

Provider Stack (main.tsx)

<QueryClientProvider>
  <FluentProvider theme={webLightTheme}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </FluentProvider>
</QueryClientProvider>

See .github/instructions/03-components.instructions.md for layout patterns, data tables, error boundaries, and accessibility guidance.

Deployment & CI/CD

From local dev to production with GitHub Actions.

Flow

Feature Branch β†’ PR β†’ CI (lint + test + build) β†’ Merge β†’ Deploy to Dev
                                                              ↓
                                          Deploy to Test β†’ Deploy to Prod

Manual Deploy (Dev Only)

⚠️
All pushes require user auth. pac code push is rejected with SPN auth for both first and subsequent pushes. The wizard creates a user profile automatically. For CI/CD, see Enable Code Apps and the deployment instructions for workarounds.
npm run deploy

GitHub Secrets Required

SecretDescription
PP_APP_IDApplication (client) ID
PP_CLIENT_SECRETClient secret value
PP_TENANT_IDDirectory (tenant) ID

Per-Environment Variable

VariableDescription
POWER_PLATFORM_URLEnvironment URL (e.g. https://org-dev.crm.dynamics.com)

See .github/instructions/04-deployment.instructions.md for CI/CD workflow YAML, solution management, and deployment settings files.

Testing

Vitest for units, Playwright for E2E, MSW for connector mocking.

Vitest + React Testing Library Playwright MSW (Mock Service Worker) v8 coverage (70% threshold)

What to Test

  • βœ… Component rendering with different states
  • βœ… User interactions (clicks, form submissions)
  • βœ… Custom hooks wrapping connectors
  • βœ… Utility functions with business logic
  • ❌ Generated files (src/generated/)
  • ❌ Fluent UI internals

See .github/instructions/05-testing.instructions.md for config files, MSW handler patterns, and CI integration.

Security

Auth is automatic. Your job: don't leak secrets, validate inputs, sanitize output.

πŸ”’
Never hardcode: API keys, tokens, client secrets, connection strings, passwords, or certificates. Use 1Password or .env.local.

Required .gitignore Entries

.env.local
.env.*.local
.pac/
auth.json
node_modules/
dist/
coverage/
.wizard-state.json

Input Validation

// Always validate user inputs
if (/<script/i.test(name)) return 'Invalid characters';
if (name.length > 200) return 'Max 200 characters';

// Never use dangerouslySetInnerHTML with user data
<Text>{project.description}</Text>  // Safe β€” React escapes

See .github/instructions/06-security.instructions.md for error message guidelines, DLP handling, and code review checklist.

Dataverse Schema

Tables, columns, option sets β€” and the rules that prevent expensive mistakes.

Golden Rules

  • Always global option sets β€” never inline choices
  • Always use publisher prefix β€” mybrn_ideastatus, not ideastatus
  • Option values start at 100000000 β€” never 0 or 1
  • Add, don't delete β€” removing a live option value breaks existing records silently
  • Create inside the solution β€” never from the top-level Tables view

Naming Convention

βœ…  mybrn_ideastatus          (prefix + descriptive, all lowercase)
βœ…  mybrn_complexitylevel
❌  IdeaStatus                (no prefix β€” collides)
❌  mybrn_IdeaStatus          (camelCase β€” use all lowercase)

See .github/instructions/07-dataverse-schema.instructions.md for idempotent setup scripts, column creation patterns, and relationship modeling.

Copilot Studio Agent Integration

Add a conversational AI agent to your Code App using the Microsoft Copilot Studio connector.

ℹ️
Copilot Studio agents are invoked through a Power Platform connector β€” the same sandbox rules apply. No direct HTTP. The generated CopilotStudioService handles everything.

Setup Steps

  1. Create & publish your agent in β†— Copilot Studio
  2. Get the agent name from Channels β†’ Web app β†’ connection string (e.g. cr3e1_customerSupportAgent)
  3. Create a connection in make.powerapps.com β†’ Connections β†’ + New β†’ "Microsoft Copilot Studio"
  4. Add the data source:
    pac code add-data-source -a "shared_microsoftcopilotstudio" -c <connectionId>
  5. Import & call the generated service in your React code

Invoking the Agent

🚨
Always use ExecuteCopilotAsyncV2. The other methods (ExecuteCopilot, ExecuteCopilotAsync) have known issues β€” fire-and-forget or 502 errors.
import { CopilotStudioService } from '@/generated/services/CopilotStudioService';

const response = await CopilotStudioService.ExecuteCopilotAsyncV2({
  message: "What is the status of my order?",
  notificationUrl: "https://notificationurlplaceholder",
  agentName: "cr3e1_customerSupportAgent",
});

if (response.data.completed) {
  console.log(response.data.lastResponse);
}

Request Parameters

ParameterRequiredDescription
messageYesUser's message. Can be JSON string for structured data.
notificationUrlYesAlways "https://notificationurlplaceholder"
agentNameYesPublished agent name (case-sensitive, includes publisher prefix)

Response Structure

PropertyTypeDescription
responsesstring[]Array of response strings from the agent
conversationIdstringConversation ID for multi-turn tracking
lastResponsestringMost recent agent response
completedbooleanWhether the agent finished processing
⚠️
Response casing varies. Property names may appear as conversationId, ConversationId, or conversationID. Always use optional chaining or a safe accessor.

Prototype Mode (Mock Data)

When running npm run dev:local, create a mock that simulates agent responses with realistic delays. The instruction file includes a complete mock implementation and shows how to wire it into useCopilotAgent with the VITE_USE_MOCK toggle.

See .github/instructions/08-copilot-studio.instructions.md for the complete hook, chat UI component, structured I/O patterns, and troubleshooting.

Naming Conventions

Quick reference for file, component, and Dataverse naming.

TypeConventionExample
ComponentsPascalCase folder + filecomponents/ProjectCard/ProjectCard.tsx
HookscamelCase + use prefixhooks/useProjects.ts
UtilscamelCaseutils/formatDate.ts
TypesPascalCasetypes/ProjectExtended.ts
ConstantsSCREAMING_SNAKE in fileconstants/routes.ts
TestsSame name + .testProjectCard.test.tsx
Dataverse tablesprefix_tablenamemybrn_project
Option setsprefix_descriptivenamemybrn_ideastatus

Common Connector IDs

Use these when creating deployment settings files or connection references.

ConnectorInternal NameConnectorId
Office 365 Usersshared_office365users/providers/Microsoft.PowerApps/apis/shared_office365users
Office 365 Outlookshared_office365/providers/Microsoft.PowerApps/apis/shared_office365
SharePointshared_sharepointonline/providers/Microsoft.PowerApps/apis/shared_sharepointonline
Microsoft Dataverseshared_commondataserviceforapps/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps
SQL Servershared_sql/providers/Microsoft.PowerApps/apis/shared_sql
Microsoft Teamsshared_teams/providers/Microsoft.PowerApps/apis/shared_teams
Azure Blob Storageshared_azureblob/providers/Microsoft.PowerApps/apis/shared_azureblob
HTTP with Entra IDshared_webcontents/providers/Microsoft.PowerApps/apis/shared_webcontents

To find Connection IDs in the Power Apps Maker Portal, look at the URL after creating a connection:

https://make.powerapps.com/environments/xxx/connections/shared_office365users/CONNECTION_ID/details

What's Next

After the wizard finishes, here's your daily workflow and common commands.

Daily Development

CommandWhat It Does
npm run dev:localPrototype with mock data β€” no auth or Power Platform connection needed
npm run devConnected mode β€” runs Vite + pac code run concurrently
npm run buildTypeScript check + Vite production build β†’ dist/
npm run testRun unit tests with Vitest
npm run lintESLint check across src/

Deploying

CommandWhat It Does
pac code pushUpload your built app to Power Platform
npm run deployShortcut: build + push in one command
⚠ SPN auth note: pac code push requires Power Platform API access that service principal (SPN) auth lacks for first-push and pac code mutation flows. Use the repo-scoped interactive profile for that environment, then push:
pac auth create --name pp-myrepo-d-u-1a2b3c4d --environment <your-env-url> --deviceCode
pac auth select --name pp-myrepo-d-u-1a2b3c4d
pac code push

Connectors & Connection References

The wizard auto-creates connection references in your solution so they travel across environments. This now happens in an explicit later connector-binding pass, not during the initial scaffold.

ActionHow
Add connectors via wizardnode wizard/index.mjs --from 8 (runs the dedicated connector-binding step)
Add Dataverse table manuallypac code add-data-source -a dataverse -t <table_name>
Add other connector manuallypac code add-data-source -a <api_id> -c <conn_id>
Map references after importPower Apps Maker Portal → Solutions → Connection References
How it works: A connection reference says "this solution uses the Office 365 Users connector." The actual authenticated connection (your credentials) is mapped to the reference per-environment. This means you deploy once and map per-environment β€” no secrets in the solution.

--apiId (-a) is always required for manual adds β€” there is no interactive picker in the CLI. The wizard compensates for this by discovering existing environment connections with pac connection list and letting you select one when possible. See the Connector IDs table above for common API IDs.

Re-running the Wizard

Both the terminal wizard and the browser wizard (npm run wizard:ux) share .wizard-state.json, so you can switch between them freely. The browser wizard lets you jump directly to any step from the step navigator.

CommandWhat It Does
node wizard/index.mjsResume from where you left off
node wizard/index.mjs --from 7Re-run from scaffold onward, including the later connector and deploy steps
node wizard/index.mjs --from 8Run the dedicated connector-binding pass and select real connections
node wizard/index.mjs --resetStart completely fresh
npm run wizard:uxOpen the browser wizard β€” jump to any step from the step navigator

Setting Up CI/CD

See .github/instructions/04-deployment.instructions.md for full details. You'll need:

  • GitHub secrets: PP_APP_ID, PP_CLIENT_SECRET, PP_TENANT_ID
  • Environment variable per target: POWER_PLATFORM_URL