Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Power Apps Code Apps
https://github.com/microsoft/powerappscodeapps
Admin
Power Apps code apps enable developers to build Power Apps applications using code, providing a
...
Tokens:
18,203
Snippets:
88
Trust Score:
9.5
Update:
1 month ago
Context
Skills
Chat
Benchmark
71.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Power Apps Code Apps Power Apps Code Apps is a Microsoft toolkit for building custom web applications that run natively within Power Apps using standard web technologies like React, TypeScript, and Vite. It enables developers to leverage Power Platform connectors and data sources (Dataverse, SharePoint, Teams, Office 365, etc.) while using modern frontend development practices. The toolkit includes project templates, sample applications, and a Claude/Copilot plugin for AI-assisted development. The repository provides two starter templates (a full-featured starter with Tailwind CSS and TanStack Query, and a minimal Vite template), five sample applications demonstrating real-world patterns, and an AI plugin that automates scaffolding, data source configuration, and deployment. The generated TypeScript services handle authentication and API communication automatically, letting developers focus on building business logic and UI components. --- ## Scaffolding a New Code App Use `npx degit` to download the starter template and initialize a new Power Apps code app project with React and Vite. ```bash # Create a new code app project npx degit microsoft/PowerAppsCodeApps/templates/starter my-app cd my-app npm install # Start local development server npm run dev # Initialize with Power Platform (requires pac CLI) pwsh -NoProfile -Command "pac code init --displayName 'My App' -e <environment-id>" # Build for production npm run build # Deploy to Power Apps pwsh -NoProfile -Command "pac code push" # Output: App URL: https://apps.powerapps.com/play/e/<env-id>/app/<app-id> ``` --- ## Adding Dataverse Data Source Connect to Dataverse tables to perform CRUD operations with auto-generated TypeScript models and services. ```bash # Add a Dataverse table as data source pwsh -NoProfile -Command "pac code add-data-source -a dataverse -t contact" # This generates: # - src/generated/models/ContactsModel.ts (TypeScript interfaces) # - src/generated/services/ContactsService.ts (CRUD methods) ``` ```typescript // Using the generated ContactsService for CRUD operations import { ContactsService } from '../generated/services/ContactsService'; import type { Contacts } from '../generated/models/ContactsModel'; // READ: Fetch contacts with filtering and pagination const result = await ContactsService.getAll({ select: ['contactid', 'firstname', 'lastname', 'emailaddress1', 'jobtitle'], filter: "statecode eq 0", // Active contacts only orderBy: ['createdon desc'], top: 50 }); const contacts: Contacts[] = result.data || []; // READ: Get single contact by ID const contact = await ContactsService.get('12345-guid-67890', { select: ['firstname', 'lastname', 'emailaddress1'] }); // CREATE: Add new contact with lookup field const newContact: any = { firstname: 'John', lastname: 'Doe', emailaddress1: 'john.doe@example.com', jobtitle: 'Developer', // Set lookup relationship using OData bind syntax 'msa_managingpartnerid@odata.bind': '/accounts(account-guid-here)' }; const createResult = await ContactsService.create(newContact); // UPDATE: Modify existing contact const updates = { jobtitle: 'Senior Developer', telephone1: '+1-555-0123' }; await ContactsService.update('contact-guid', updates); // DELETE: Remove contact await ContactsService.delete('contact-guid'); ``` --- ## Handling Dataverse Choice/Picklist Fields Choice fields store integer values, not string labels. Use numeric constants for filtering and creating records. ```typescript // CRITICAL: Choice fields use numeric values, NOT string labels // Check the generated model for enum mappings // Define constants matching Dataverse picklist values const Status = { Active: 0, Inactive: 1, Pending: 2 } as const; // CORRECT: Filter using numeric values const activeRecords = await ContactsService.getAll({ filter: "statuscode eq 0" // Active status }); // CORRECT: Create record with numeric choice value const newRecord: any = { 'prefix_name': 'My Record', 'prefix_category': 100000000, // Use numeric value from schema 'statuscode': Status.Active }; // CORRECT: Convert numeric to label for display const getStatusLabel = (status?: number): string => { switch (status) { case Status.Active: return 'Active'; case Status.Inactive: return 'Inactive'; case Status.Pending: return 'Pending'; default: return 'Unknown'; } }; // WRONG: This fails - cannot compare number to string // records.filter(r => r.statuscode === 'Active'); // WRONG: API rejects string values // { 'prefix_category': 'Electronics' } // Error: Cannot convert to Edm.Int32 ``` --- ## Reading Dataverse Lookup Fields Lookup fields expose three properties: navigation property, `_value` GUID, and display name. ```typescript // Lookup fields return related record GUIDs via _<fieldname>_value pattern const result = await AccountsService.getAll({ select: ['name', '_primarycontactid_value', 'primarycontactidname'] }); for (const account of result.data || []) { // CORRECT: Read the related record's GUID via _value property if (account._primarycontactid_value) { const contact = await ContactsService.get(account._primarycontactid_value); console.log(`Primary contact: ${contact.data?.fullname}`); } // Use the *name property for display (no extra query needed) console.log(`Contact display name: ${account.primarycontactidname}`); } // Common lookup fields: // _primarycontactid_value, _customerid_value, _ownerid_value // _parentaccountid_value, _transactioncurrencyid_value ``` --- ## Adding SharePoint Online Connector Connect to SharePoint lists and document libraries with auto-generated TypeScript services. ```bash # Step 1: List existing connections pwsh -NoProfile -Command "pac connection list" # → Find SharePoint Online connection ID (e.g., conn-sp-xyz789) # Step 2: Discover available sites pwsh -NoProfile -Command "pac code list-datasets -a sharepointonline -c conn-sp-xyz789" # → Lists sites: https://contoso.sharepoint.com/sites/Projects # Step 3: Discover tables/lists in a site pwsh -NoProfile -Command "pac code list-tables -a sharepointonline -c conn-sp-xyz789 -d 'https://contoso.sharepoint.com/sites/Projects'" # → Lists: Project Milestones, Documents, Team Wiki # Step 4: Add the connector for a specific list pwsh -NoProfile -Command "pac code add-data-source -a sharepointonline -c conn-sp-xyz789 -d 'https://contoso.sharepoint.com/sites/Projects' -t 'Project Milestones'" ``` ```typescript // Using the generated SharePointOnlineService import { SharePointOnlineService } from '../generated/services/SharePointOnlineService'; // Get items from a SharePoint list const items = await SharePointOnlineService.GetItems({ dataset: "https://contoso.sharepoint.com/sites/Projects", table: "Project Milestones" }); const milestones = items.data?.value || []; // Create a new list item await SharePointOnlineService.PostItem({ dataset: "https://contoso.sharepoint.com/sites/Projects", table: "Project Milestones", item: { Title: "Phase 1 Complete", Description: "Initial development milestone", Status: "Active", // Choice columns use string values (unlike Dataverse) DueDate: "2024-06-30" } }); // List files from a document library const files = await SharePointOnlineService.ListFolder({ dataset: "https://contoso.sharepoint.com/sites/Projects", id: "Shared Documents" }); // Get file content const content = await SharePointOnlineService.GetFileContent({ dataset: "https://contoso.sharepoint.com/sites/Projects", id: "/sites/Projects/Shared Documents/report.pdf" }); ``` --- ## Adding Office 365 Outlook Connector Connect to Office 365 for email, calendar, and contact operations. ```bash # Add Office 365 Outlook connector pwsh -NoProfile -Command "pac connection list" # → Find Office 365 Outlook connection ID pwsh -NoProfile -Command "pac code add-data-source -a office365 -c <connection-id>" ``` ```typescript // Using the generated Office365OutlookService import { Office365OutlookService } from '../generated/services/Office365OutlookService'; // Get calendar events const events = await Office365OutlookService.GetEventsCalendarViewV2({ calendarId: 'Calendar', startDateTime: '2024-01-01T00:00:00Z', endDateTime: '2024-12-31T23:59:59Z' }); // Send an email await Office365OutlookService.SendEmailV2({ to: 'recipient@example.com', subject: 'Meeting Reminder', body: '<p>Don\'t forget our meeting tomorrow at 2pm.</p>', isHtml: true }); // Get inbox messages const messages = await Office365OutlookService.GetEmails({ folderPath: 'Inbox', top: 25 }); ``` --- ## Adding Microsoft Teams Connector Send and read Teams messages from your Power Apps code app. ```bash # Add Teams connector pwsh -NoProfile -Command "pac code add-data-source -a teams -c <connection-id>" ``` ```typescript // Using the generated TeamsService import { TeamsService } from '../generated/services/TeamsService'; // Post a message to a Teams channel await TeamsService.PostMessageToChannelV3({ groupId: 'team-guid', channelId: 'channel-id', body: { content: 'Build completed successfully!', contentType: 'text' } }); // Get messages from a channel const messages = await TeamsService.GetMessagesFromChannel({ groupId: 'team-guid', channelId: 'channel-id' }); ``` --- ## Custom React Hook for Dataverse CRUD Create a reusable hook that encapsulates business logic for Dataverse entity operations. ```typescript import { useState, useEffect } from 'react'; import { ContactsService } from '../generated/services/ContactsService'; import type { Contacts } from '../generated/models/ContactsModel'; interface ContactFormData { firstname: string; lastname: string; emailaddress1?: string; telephone1?: string; managingpartnerid?: string; } export function useContacts() { const [contacts, setContacts] = useState<Contacts[]>([]); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); // Load contacts on mount useEffect(() => { loadContacts(); }, []); const loadContacts = async () => { try { setLoading(true); setError(null); const result = await ContactsService.getAll({ select: ['contactid', 'firstname', 'lastname', 'emailaddress1'], orderBy: ['createdon desc'], top: 50 }); setContacts(result.data || []); } catch (err) { setError(`Error loading contacts: ${(err as Error).message}`); console.error('Error loading contacts:', err); } finally { setLoading(false); } }; const createContact = async (formData: ContactFormData): Promise<boolean> => { try { setError(null); const newContact: any = { firstname: formData.firstname, lastname: formData.lastname, emailaddress1: formData.emailaddress1 || undefined, telephone1: formData.telephone1 || undefined }; // Set lookup using OData bind syntax if (formData.managingpartnerid?.trim()) { newContact['msa_managingpartnerid@odata.bind'] = `/accounts(${formData.managingpartnerid})`; } const result = await ContactsService.create(newContact); if (result.data) { await loadContacts(); return true; } return false; } catch (err) { setError(`Error creating contact: ${(err as Error).message}`); return false; } }; const deleteContact = async (contactId: string): Promise<boolean> => { try { setError(null); await ContactsService.delete(contactId); await loadContacts(); return true; } catch (err) { setError(`Error deleting contact: ${(err as Error).message}`); return false; } }; return { contacts, loading, error, loadContacts, createContact, deleteContact }; } ``` --- ## Building and Deploying Build the application and deploy to Power Platform with version management. ```bash # Build the application (TypeScript compilation + Vite bundling) npm run build # Outputs to dist/ folder with index.html and assets/ # Deploy to Power Apps pwsh -NoProfile -Command "pac code push" # Output: https://apps.powerapps.com/play/e/<env-id>/app/<app-id> # If authentication fails, re-authenticate: pwsh -NoProfile -Command "pac auth list" # Check current auth state pwsh -NoProfile -Command "pac auth clear" # Clear existing profiles pwsh -NoProfile -Command "pac auth create" # Create new auth profile # Switch to a different environment: pwsh -NoProfile -Command "pac env list" # List available environments pwsh -NoProfile -Command "pac env select --environment <environment-id>" pwsh -NoProfile -Command "pac code push" # Deploy to selected environment # Mac fallback (if pac auth issues): npm install -g @microsoft/power-apps-cli npx power-apps push ``` --- ## Inspecting Generated Service Files Generated service files can be large. Use grep patterns to find specific methods instead of reading entire files. ```bash # List all available methods in a generated service grep -E "async \w+" src/generated/services/SharePointOnlineService.ts # Find a specific method with context grep -A 20 "async GetItems" src/generated/services/SharePointOnlineService.ts # Find parameter types in models grep -A 30 "interface CalendarEvent" src/generated/models/Office365OutlookModel.ts ``` ```typescript // Generated services follow consistent patterns: // - Static class with async methods // - All methods return Promise<IOperationResult<T>> // - Access result.data for actual data import { ContactsService } from '../generated/services/ContactsService'; // Service methods: // - create(record): Create new record // - get(id, options?): Get single record by ID // - getAll(options?): Get multiple records with filtering // - update(id, changedFields): Update existing record // - delete(id): Delete record // - getMetadata(options?): Get entity metadata const result = await ContactsService.getAll({ select: ['contactid', 'fullname'], // Fields to return filter: "contains(fullname, 'Smith')", // OData filter orderBy: ['fullname asc'], // Sort order top: 100, // Max records skip: 0 // Pagination offset }); if (result.data) { console.log(`Found ${result.data.length} contacts`); } ``` --- ## Component Architecture Pattern Follow a three-layer architecture: Components (UI), Hooks (business logic), and Services (data access). ```typescript // App.tsx - Wires together components and hooks import { Header, ContactList, ContactForm } from "./components"; import { useContacts, useAccounts } from "./hooks"; function App() { // Hooks manage all business logic and state const { contacts, loading, error, selectedContact, isCreating, startCreate, selectContact, handleFormSubmit, deleteContact } = useContacts(); // Load related data for dropdowns const { accounts } = useAccounts(); return ( <div className="app-container"> <Header title="Contact Manager" /> {error && <div className="error">{error}</div>} <div className="content-grid"> <ContactList contacts={contacts} selectedContact={selectedContact} loading={loading} onSelect={selectContact} onCreateNew={startCreate} /> {(isCreating || selectedContact) && ( <ContactForm selectedContact={selectedContact} isCreating={isCreating} accounts={accounts} onSubmit={handleFormSubmit} onDelete={deleteContact} /> )} </div> </div> ); } // Data flow: // User Action -> Component -> Hook -> Service -> Dataverse // Dataverse -> Service -> Hook -> Component -> UI Update ``` --- Power Apps Code Apps enables developers to build enterprise-grade web applications that integrate seamlessly with the Microsoft Power Platform ecosystem. The primary use cases include building custom business applications with Dataverse as the backend, creating dashboards that aggregate data from multiple Microsoft 365 services (SharePoint, Teams, Outlook), and extending Power Apps with modern React-based interfaces that go beyond what low-code tools can provide. Integration patterns center around the auto-generated TypeScript services that abstract away authentication, API calls, and data transformation. Developers connect to data sources using the `pac code add-data-source` command, which generates type-safe service classes. The recommended architecture separates concerns into presentation components, custom hooks for business logic, and generated services for data access. For production deployments, use `npm run build` followed by `pac code push` to deploy to your Power Platform environment, with the app accessible via the generated Power Apps URL.