Power Apps Code Apps — Part 2: Connectors & Outlook
Full email client in a code app, built with React + Fluent UI v9, powered by the Outlook connector.
This is the companion repo for Part 2 of the blog series. Make sure you have completed Part 1 before continuing.
What you will build
An Outlook-like email client with:
- Folder navigation — Inbox, Sent, Drafts, Trash
- Email list — unread indicators, avatar colors, relative timestamps
- Reading pane — full email body (HTML-safe) with Reply / Forward / Delete
- Compose dialog — Fluent UI v9 dialog with validation
- Connections panel — visual reference of connectors + PAC CLI commands
- Real-time search — filter by sender or subject
All built with Fluent UI v9 (@fluentui/react-components) and the Outlook connector.
Prerequisites
| Requirement | Version |
|---|---|
| Node.js | 18+ |
| npm | 9+ |
| PAC CLI | 2.6+ (dotnet tool install -g Microsoft.PowerApps.CLI) |
| Power Apps environment | Licensed user with code apps enabled |
| Microsoft 365 account | For the Outlook connector |
Quick start
# 1. Clone & install
git clone <this-repo>
cd power-apps-codeapps-blog-part2
npm install
# 2. Run locally (UI only, no connector data)
npm run dev
# 3. Run locally WITH real connector data
# Terminal 1:
npm run dev
# Terminal 2:
pac code run -a http://localhost:5173
# 4. Open the URL printed by pac code run in your browser
Project structure
power-apps-codeapps-blog-part2/
├── index.html HTML shell
├── package.json Dependencies & scripts
├── tsconfig.json TypeScript config
├── vite.config.ts Vite dev server & build config
├── power.config.json Power Platform app manifest (required by PAC CLI)
├── .gitignore Excludes dist/, .power/, node_modules/
└── src/
├── main.tsx Entry point — FluentProvider + light/dark theme
├── App.tsx Main UI: nav rail, folders, email list, reading pane
├── index.css Minimal reset (Fluent UI handles component styles)
├── shims/
│ └── power-apps-data.ts Local dev shim for @microsoft/power-apps/data
├── types/
│ ├── index.ts Shared types (Email, Folder, FOLDER_PATH_MAP)
│ └── power-apps.d.ts Type declarations for the Power Platform runtime
├── services/
│ └── OutlookService.ts Adapter wrapping the generated Outlook_comService
└── generated/ Auto-generated by pac code add-data-source
├── index.ts
├── models/
│ └── Outlook_comModel.ts Typed request/response interfaces
└── services/
└── Outlook_comService.ts All connector operations as static methods
Architecture
┌──────────────────────────────────────────────────────┐
│ Your Code App │
│ │
│ React + Fluent UI → OutlookService → Generated │
│ (adapter) Service │
│ ↕ │
│ ┌───────────────────────────────────┐ │
│ │ Power Platform Runtime │ │
│ │ ───────────────────────────── │ │
│ │ @microsoft/power-apps/data │ │
│ │ → Outlook connector API │ │
│ └───────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
src/services/OutlookService.ts wraps the auto-generated Outlook_comService and maps its responses to the UIs Email` type. All real connector calls go through the Power Platform runtime SDK.
Setting up your own connections
1. Check your connections
pac connection list
Example output:
Connected as user@contoso.com
Id Name API Id Status
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Outlook.com /providers/Microsoft.PowerApps/apis/shared_outlook Connected
2. Add the connector to your code app
pac code add-data-source \
-a shared_outlook \
-c <your-connection-id>
This auto-generates typed files in src/generated/:
src/generated/
├── models/Outlook_comModel.ts ← request/response types
└── services/Outlook_comService.ts ← typed service with all operations
3. Update power.config.json
Replace the placeholder values with your real IDs:
{
"environmentId": "<your-environment-id>",
"connectionReferences": {
"<your-connection-reference-id>": {
"id": "/providers/Microsoft.PowerApps/apis/shared_outlook",
...
}
}
}
4. Run with real connections
# Terminal 1 — Vite dev server
npm run dev
# Terminal 2 — Power Platform connection proxy
pac code run -a http://localhost:5173
Open the URL printed by pac code run in your browser.
Removing an unused data source / connection
If you added a connection by mistake, or the schema changed and you need to refresh:
Delete the data source
pac code delete-data-source -a <apiName> -ds <dataSourceName>
For example, to remove an Outlook data source named outlook:
pac code delete-data-source -a shared_outlook -ds outlook
This removes:
- The entry from
power.config.json→connectionReferences - The generated files under
src/generated/
Clean up manually (if needed)
If the CLI does not fully clean up:
power.config.json— Remove the connection reference entry fromconnectionReferencessrc/generated/— Delete the corresponding*Model.tsand*Service.tsfiles.power/schemas/— Delete the connector schema folder (e.g.,.power/schemas/outlook/)src/services/OutlookService.ts— Remove or update the adapter that imports from the deleted generated service
Verify
npx tsc --noEmit # should compile without errors
npm run build # should produce a clean build
Important
: There is no
refreshcommand yet. If a connector schema changes, delete the data source and re-add it.
Available connector methods
After running pac code add-data-source, the generated Outlook_comService.ts exposes these static methods:
| Method | Description |
|---|---|
GetEmails(folderPath, ...) |
Get emails from a folder |
GetEmailsV2(folderPath, ...) |
V2 with more filter options |
GetEmail(messageId) |
Get a single email by ID |
SendEmail(message) |
Send email (plain text) |
SendEmailV2(message) |
Send email (HTML) |
MarkAsRead(messageId) |
Mark as read |
DeleteEmail(messageId) |
Delete email |
Move(messageId, folderPath) |
Move to another folder |
Flag(messageId) |
Flag an email |
ReplyTo / ReplyToV2 / ReplyToV3 |
Reply (text/HTML) |
ForwardEmail(messageId, body) |
Forward |
All methods return Promise<IOperationResult<T>> — access the data via result.data.
Connection references
Code apps use connection references instead of binding to user-specific connections:
| Benefit | Description |
|---|---|
| Environment promotion | Connections change per env (dev/staging/prod) without code changes |
| Managed identity | System-assigned identities instead of user credentials |
| Governance | IT audits via DLP policies |
Deploy to Power Platform
# Build and push to your environment
npm run build
pac code push
To target a specific solution:
pac code push --solution <solution-id>
NPM scripts
| Script | Description |
|---|---|
npm run dev |
Start Vite dev server at http://localhost:5173 |
npm run build |
Type-check + production build |
npm run preview |
Preview the production build locally |
Key differences from Canvas Apps
| Aspect | Canvas Apps | Code Apps |
|---|---|---|
| UI Framework | Power Fx formula bar | React / Vue / Svelte |
| Connector access | Built-in connector picker | PAC CLI + typed client library |
| Data model | Implicit, delegation-based | Explicit TypeScript types |
| Styling | Themed controls | Fluent UI / any CSS framework |
| Deployment | Packaged by Power Platform | pac code push |
| Source control | Limited | Full git-native workflow |
References
- How to: Connect your code app to data — Microsoft Learn
- Power Apps code apps overview — Microsoft Learn
- Code apps architecture — Microsoft Learn
- pac code add-data-source — PAC CLI reference
- pac code delete-data-source — PAC CLI reference
- pac connection list — PAC CLI reference
- Connector classification (DLP) — Microsoft Learn
- Fluent UI v9 — React components — Microsoft
- Part 1: Power Apps code apps tutorial — That's a good question