Embed Power BI reports in your Lovable app or push data to Power BI datasets using Supabase Edge Functions. The app-owns-data embedding pattern uses an Azure AD service principal, Edge Functions generate embed tokens via the Power BI REST API, and the Power BI JavaScript SDK renders reports in your React frontend. Store Azure credentials in Cloud → Secrets. Use Power BI embedding when stakeholders need Power BI's advanced analytics while the app requires a modern Lovable-built interface around them.
Embed Power BI reports inside your Lovable app with secure token generation
Power BI is Microsoft's dominant enterprise analytics platform — most large organizations have significant investments in Power BI reports, datasets, and dashboards built over years by their data teams. When an organization wants to modernize the interface around these analytics — building a customer-facing portal, a polished internal application, or a specialized tool that uses Power BI reports as a component — embedding Power BI inside a Lovable app provides the best of both worlds: Power BI's advanced analytics capabilities within a custom Lovable-built user experience.
The app-owns-data embedding pattern is the appropriate approach for embedding Power BI in applications where your users may not have Power BI Pro licenses or Azure AD accounts. In this pattern, your application (the Lovable app backed by the Supabase Edge Function) authenticates as a service principal with the Power BI API, generates a short-lived embed token for the specific report and user context, and provides that token to the frontend. The Power BI JavaScript SDK uses the token to render the report directly in the browser without the user needing their own Power BI account.
The integration has two key components: the Supabase Edge Function handles all Azure AD authentication and Power BI API calls server-side, keeping your client secret and service principal credentials safe in Cloud → Secrets. The React frontend imports the Power BI JavaScript SDK from a CDN and uses a simple React wrapper to render the embedded report using the token provided by the Edge Function. This guide also covers pushing data to Power BI datasets from Lovable — useful for triggering refreshes when your Supabase data changes and Power BI should update its reports.
Integration method
Power BI integrates with Lovable through the app-owns-data embedding pattern. An Azure AD service principal authenticates with Power BI's REST API server-side in a Supabase Edge Function. The Edge Function generates a short-lived embed token for a specific report, workspace, and dataset. The React frontend uses the Power BI JavaScript SDK to render the report using this token. Azure credentials are stored in Cloud → Secrets and never exposed to the browser.
Prerequisites
- A Lovable account with an active Lovable Cloud project
- A Microsoft Azure account with an Azure AD tenant (a free Azure account at azure.microsoft.com works for development)
- A Power BI Premium capacity or Power BI Embedded SKU (embedding requires Power BI Premium Per User, Power BI Premium, or Azure Power BI Embedded A SKU — the free Power BI Desktop does not support embedding)
- An existing Power BI report in a Power BI workspace that you want to embed
- An Azure AD service principal created with the Power BI application admin role
Step-by-step guide
Create an Azure AD service principal for Power BI
Create an Azure AD service principal for Power BI
The app-owns-data embedding pattern uses an Azure AD service principal (a non-interactive application identity) to authenticate with the Power BI API. The service principal acts as your application's identity for all Power BI API calls — it is the 'user' that your Edge Function logs in as to generate embed tokens. To create a service principal, log in to the Azure portal at portal.azure.com. In the search bar, type 'App registrations' and click it. Click 'New registration'. Give it a name like 'Lovable Power BI Integration'. For supported account types, select 'Accounts in this organizational directory only'. Click Register. After registration, you are on the app's Overview page. Note three values: - Application (client) ID — copy this, it is your AZURE_CLIENT_ID - Directory (tenant) ID — copy this, it is your AZURE_TENANT_ID Now create a client secret. In the left sidebar, click 'Certificates & secrets' → 'New client secret'. Give it a description and set the expiry to 24 months (the maximum). Click Add. Copy the Value immediately — this is your AZURE_CLIENT_SECRET. It is shown only once. Next, enable the service principal to access Power BI. In the Power BI Admin Portal (app.powerbi.com → Settings → Admin portal), go to 'Tenant settings' → 'Developer settings' → 'Allow service principals to use Power BI APIs' and enable it. Apply to the security group that contains your service principal, or apply to the entire organization for development. Finally, add the service principal to your Power BI workspace. In Power BI Service (app.powerbi.com), navigate to the workspace containing your reports → Settings → Access → add the service principal as a 'Member' or 'Admin'. Use the service principal's name (the name you gave the app registration).
Pro tip: Copy the client secret Value immediately after creating it — Azure only shows it once. If you lose it, you must create a new client secret and update your Cloud → Secrets.
Expected result: An Azure AD service principal is created with a client ID, tenant ID, and client secret. The service principal has Power BI API access enabled in the Power BI Admin Portal and is a Member of the workspace containing your reports.
Store Azure and Power BI credentials in Cloud → Secrets
Store Azure and Power BI credentials in Cloud → Secrets
Store your Azure service principal credentials in Lovable's Cloud Secrets panel. These credentials authenticate your Edge Function as the service principal — anyone with these values can generate embed tokens for your Power BI workspace, so they must be treated as highly sensitive and never included in frontend code. Lovable's platform blocks approximately 1,200 hardcoded API keys per day and holds SOC 2 Type II and ISO 27001:2022 certifications. The Secrets panel provides encrypted storage accessible only from Edge Functions via Deno.env.get(). Click the '+' icon next to the Preview label in the Lovable editor. Go to Cloud → Secrets. Add the following secrets: - Name: AZURE_TENANT_ID — Value: your Azure AD Directory (tenant) ID (looks like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) - Name: AZURE_CLIENT_ID — Value: your app registration Application (client) ID - Name: AZURE_CLIENT_SECRET — Value: your service principal client secret value - Name: POWERBI_WORKSPACE_ID — Value: your Power BI workspace ID (found in the workspace URL at app.powerbi.com/groups/YOUR-WORKSPACE-ID/) - Name: POWERBI_REPORT_ID — Value: the ID of the specific report to embed (in the report URL at app.powerbi.com/groups/WORKSPACE-ID/reports/YOUR-REPORT-ID) For a multi-report application where different pages embed different reports, you can pass POWERBI_REPORT_ID as a parameter from the frontend rather than storing it as a secret — report IDs are not credentials, just identifiers. Only the authentication credentials (tenant ID, client ID, client secret) must be in Secrets.
Pro tip: Store POWERBI_WORKSPACE_ID and POWERBI_REPORT_ID as secrets initially for simplicity, then refactor to pass them as parameters once the integration is working — this makes it easier to embed multiple different reports.
Expected result: Five secrets — AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, POWERBI_WORKSPACE_ID, and POWERBI_REPORT_ID — are stored in Cloud → Secrets. Edge Functions can access all five via Deno.env.get().
Create an Edge Function to generate Power BI embed tokens
Create an Edge Function to generate Power BI embed tokens
The embed token Edge Function has three stages: obtain an Azure AD access token using the client credentials OAuth flow, use that token to call the Power BI API to get the report's embed URL, and then call the Power BI GenerateToken API to create a short-lived embed token scoped to the specific report and user context. The Azure AD token endpoint accepts a POST request with the client credentials (tenant ID, client ID, client secret) and returns an access token with a 1-hour expiry. This token then authenticates the Power BI API calls. The GenerateToken API returns an embed token with a configurable expiry (30 minutes default) that the frontend Power BI SDK uses to render the report. For row-level security (RLS) embedding where different users see different data, the GenerateToken call includes an effective identity with the username and roles. The Power BI dataset must have RLS configured with matching role names. The Edge Function receives the user's identity information and passes it through to Power BI. This Edge Function also supports a dataset refresh trigger by accepting an optional action parameter — when action is 'refresh', it calls the Power BI Dataset Refresh API instead of generating an embed token.
Create a Supabase Edge Function at supabase/functions/powerbi-embed/index.ts that generates Power BI embed tokens for the app-owns-data pattern. Read AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, POWERBI_WORKSPACE_ID, and POWERBI_REPORT_ID from Deno.env.get. Step 1: POST to Azure AD token endpoint to get an access token using client credentials. Step 2: GET the Power BI report embed URL. Step 3: POST to Power BI GenerateToken API. Return the embed token, embed URL, and report ID. Include CORS headers.
Paste this in Lovable chat
1// supabase/functions/powerbi-embed/index.ts2const corsHeaders = {3 'Access-Control-Allow-Origin': '*',4 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',5};67async function getAzureToken(): Promise<string> {8 const tenantId = Deno.env.get('AZURE_TENANT_ID')!;9 const clientId = Deno.env.get('AZURE_CLIENT_ID')!;10 const clientSecret = Deno.env.get('AZURE_CLIENT_SECRET')!;1112 const tokenRes = await fetch(13 `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,14 {15 method: 'POST',16 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },17 body: new URLSearchParams({18 grant_type: 'client_credentials',19 client_id: clientId,20 client_secret: clientSecret,21 scope: 'https://analysis.windows.net/powerbi/api/.default',22 }),23 }24 );2526 const tokenData = await tokenRes.json();27 if (!tokenData.access_token) throw new Error(`Azure token error: ${JSON.stringify(tokenData)}`);28 return tokenData.access_token;29}3031Deno.serve(async (req) => {32 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });3334 try {35 const body = await req.json().catch(() => ({}));36 const workspaceId = Deno.env.get('POWERBI_WORKSPACE_ID')!;37 const reportId = body.reportId || Deno.env.get('POWERBI_REPORT_ID')!;3839 const accessToken = await getAzureToken();40 const authHeader = `Bearer ${accessToken}`;4142 // Get report details (embed URL)43 const reportRes = await fetch(44 `https://api.powerbi.com/v1.0/myorg/groups/${workspaceId}/reports/${reportId}`,45 { headers: { Authorization: authHeader } }46 );47 if (!reportRes.ok) throw new Error(`Get report failed: ${reportRes.status}`);48 const report = await reportRes.json();4950 // Generate embed token51 const embedBody: Record<string, unknown> = {52 accessLevel: 'View',53 reports: [{ id: reportId }],54 };5556 // Optional RLS for row-level security57 if (body.username && body.roles) {58 const datasetId = report.datasetId;59 embedBody.identities = [{60 username: body.username,61 roles: Array.isArray(body.roles) ? body.roles : [body.roles],62 datasets: [datasetId],63 }];64 }6566 const tokenRes = await fetch(67 `https://api.powerbi.com/v1.0/myorg/groups/${workspaceId}/reports/${reportId}/GenerateToken`,68 {69 method: 'POST',70 headers: { Authorization: authHeader, 'Content-Type': 'application/json' },71 body: JSON.stringify(embedBody),72 }73 );7475 if (!tokenRes.ok) {76 const errorText = await tokenRes.text();77 throw new Error(`GenerateToken failed: ${tokenRes.status} - ${errorText}`);78 }7980 const tokenData = await tokenRes.json();8182 return new Response(JSON.stringify({83 embedToken: tokenData.token,84 embedUrl: report.embedUrl,85 reportId,86 tokenExpiry: tokenData.expiration,87 }), {88 headers: { ...corsHeaders, 'Content-Type': 'application/json' },89 });90 } catch (error) {91 console.error('Power BI embed error:', error);92 return new Response(JSON.stringify({ error: String(error) }), {93 status: 500,94 headers: { ...corsHeaders, 'Content-Type': 'application/json' },95 });96 }97});Pro tip: Embed tokens expire in 60 minutes by default. For long-running dashboard sessions, implement token refresh in the frontend: when the Power BI SDK fires a tokenExpired event, call the Edge Function again to get a new embed token and use the JavaScript SDK's setAccessToken() method to update it without reloading the report.
Expected result: The powerbi-embed Edge Function returns an embed token, embed URL, and report ID. Test by calling the function — the response should contain a valid-looking token string (not an error) and a URL that starts with https://app.powerbi.com/reportEmbed.
Embed the Power BI report in your React frontend
Embed the Power BI report in your React frontend
With the Edge Function generating embed tokens, add the Power BI JavaScript SDK to your Lovable React app and create a component that renders the report. The Power BI JavaScript SDK is a Microsoft-maintained library that handles report rendering, interaction events, and token management in the browser. For a Vite + React project, the cleanest approach is to add the powerbi-client and powerbi-client-react packages from npm, which provide a React-friendly wrapper around the SDK. Alternatively, load the powerbi-client script from a CDN and use the window.powerbi object directly. The PowerBIEmbed component from powerbi-client-react accepts the embed type ('report'), an embed config object containing the access token, embed URL, report ID, and settings, and callbacks for loaded, rendered, and error events. It renders an iframe that hosts the Power BI report. For responsive design, the report iframe should fill the available container width. Power BI reports are designed to scale, but the dashboard layout around them controls the visual context. Ask Lovable to wrap the PowerBIEmbed component in a container with appropriate height and responsive styling. Embed token refresh is handled by listening to the tokenExpired event from the Power BI SDK and calling the Edge Function to get a new token, then using the component's setAccessToken() method. Lovable's AI can generate this token refresh logic when you describe it in the chat.
Install powerbi-client and powerbi-client-react. Create a PowerBIReport React component at src/components/PowerBIReport.tsx that: (1) calls the powerbi-embed Supabase Edge Function on mount to get the embed token and URL, (2) uses the PowerBIEmbed component from powerbi-client-react to render the report with the received token, (3) shows a loading skeleton while the token is being fetched, (4) handles errors with a retry button, and (5) refreshes the token when the Power BI tokenExpired event fires. Make the report take the full width of its container and 600px height by default.
Paste this in Lovable chat
1// src/components/PowerBIReport.tsx2import { useState, useEffect, useRef } from 'react';3import { PowerBIEmbed } from 'powerbi-client-react';4import { models, Report } from 'powerbi-client';5import { supabase } from '@/integrations/supabase/client';67interface PowerBIReportProps {8 reportId?: string;9 height?: number;10}1112interface EmbedConfig {13 embedToken: string;14 embedUrl: string;15 reportId: string;16 tokenExpiry: string;17}1819export function PowerBIReport({ reportId, height = 600 }: PowerBIReportProps) {20 const [embedConfig, setEmbedConfig] = useState<EmbedConfig | null>(null);21 const [loading, setLoading] = useState(true);22 const [error, setError] = useState<string | null>(null);23 const reportRef = useRef<Report | null>(null);2425 const fetchEmbedConfig = async () => {26 setLoading(true);27 setError(null);28 try {29 const { data, error: fnError } = await supabase.functions.invoke('powerbi-embed', {30 body: reportId ? { reportId } : {},31 });32 if (fnError) throw fnError;33 setEmbedConfig(data);34 } catch (err) {35 setError(err instanceof Error ? err.message : 'Failed to load report');36 } finally {37 setLoading(false);38 }39 };4041 useEffect(() => { fetchEmbedConfig(); }, [reportId]);4243 if (loading) return <div className="animate-pulse bg-gray-100 rounded" style={{ height }} />;44 if (error) return (45 <div className="flex flex-col items-center justify-center gap-2 bg-gray-50 rounded border" style={{ height }}>46 <p className="text-sm text-gray-500">{error}</p>47 <button onClick={fetchEmbedConfig} className="text-sm text-blue-600 hover:underline">Retry</button>48 </div>49 );50 if (!embedConfig) return null;5152 return (53 <PowerBIEmbed54 embedConfig={{55 type: 'report',56 id: embedConfig.reportId,57 embedUrl: embedConfig.embedUrl,58 accessToken: embedConfig.embedToken,59 tokenType: models.TokenType.Embed,60 settings: {61 panes: { filters: { visible: false } },62 background: models.BackgroundType.Transparent,63 },64 }}65 eventHandlers={new Map([66 ['tokenExpired', () => { fetchEmbedConfig(); }],67 ['error', (event) => { console.error('Power BI error:', event?.detail); }],68 ])}69 getEmbeddedComponent={(component) => { reportRef.current = component as Report; }}70 cssClassName="powerbi-report"71 style={{ width: '100%', height }}72 />73 );74}Pro tip: Power BI reports embedded with models.TokenType.Embed (app-owns-data) do not require the viewer to have a Power BI license — the service principal's capacity covers the rendering.
Expected result: The PowerBIReport component renders the Power BI report in the Lovable app. The report is interactive — users can click through pages, interact with slicers, and drill down as they would in Power BI Service. The token refresh handler prevents the report from going blank after 60 minutes.
Add dataset refresh triggers and deploy the integration
Add dataset refresh triggers and deploy the integration
For the complete Power BI integration, add the ability to trigger a Power BI dataset refresh from Lovable when your underlying data changes. When Supabase data is updated (a new sale recorded, a customer profile changed, inventory updated), the Power BI dataset can be refreshed so the embedded reports show current data. Create a second Edge Function (or add an optional action parameter to the existing powerbi-embed function) that calls the Power BI Dataset Refresh API. This API triggers an asynchronous refresh of the specified dataset — Power BI will pull fresh data from its configured data sources. The refresh typically completes in 5-15 minutes depending on dataset size. To add the refresh trigger, add a button in the Lovable UI that calls the Edge Function. Alternatively, trigger it automatically from a Supabase database trigger or webhook when certain data changes. Before deploying to production, test the full embed flow on your published Lovable app URL. Power BI iframes must be served from HTTPS — Lovable's published apps use HTTPS by default, so this should not be an issue. If you use a custom domain, verify it has a valid SSL certificate. For complex Power BI embedding scenarios including multi-report dashboards, per-tenant RLS, dynamic report filtering through URL parameters, or Power BI Paginated Reports embedding, RapidDev's team has experience with all Power BI embedding patterns and can help configure both the Azure side and the Lovable integration.
Add a 'Refresh Data' button to the analytics page that calls the powerbi-embed Edge Function with action='refresh' to trigger a Power BI dataset refresh. After clicking, show a toast saying 'Dataset refresh started — reports will update in about 10 minutes'. Disable the button for 5 minutes after clicking to prevent multiple rapid refresh requests. Log the refresh trigger timestamp in a Supabase table for audit purposes.
Paste this in Lovable chat
1// Addition to powerbi-embed Edge Function for dataset refresh2// Add this block after the existing GenerateToken logic:34// Dataset refresh trigger (add to the beginning of Deno.serve)5if (body.action === 'refresh') {6 const accessToken = await getAzureToken();7 const reportId = body.reportId || Deno.env.get('POWERBI_REPORT_ID')!;8 const workspaceId = Deno.env.get('POWERBI_WORKSPACE_ID')!;910 // Get dataset ID from report11 const reportRes = await fetch(12 `https://api.powerbi.com/v1.0/myorg/groups/${workspaceId}/reports/${reportId}`,13 { headers: { Authorization: `Bearer ${accessToken}` } }14 );15 const report = await reportRes.json();1617 // Trigger dataset refresh18 const refreshRes = await fetch(19 `https://api.powerbi.com/v1.0/myorg/groups/${workspaceId}/datasets/${report.datasetId}/refreshes`,20 {21 method: 'POST',22 headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },23 body: JSON.stringify({ notifyOption: 'NoNotification' }),24 }25 );2627 return new Response(JSON.stringify({ success: refreshRes.ok, status: refreshRes.status }), {28 status: refreshRes.ok ? 202 : refreshRes.status,29 headers: { ...corsHeaders, 'Content-Type': 'application/json' },30 });31}Pro tip: Power BI Premium datasets support up to 48 scheduled refreshes per day. On-demand API refreshes count toward this limit. For high-frequency data, consider Power BI's DirectQuery mode (which queries the source in real-time) rather than Import mode with scheduled refreshes.
Expected result: The Refresh Data button triggers a Power BI dataset refresh via the Edge Function. Cloud → Logs shows a 202 Accepted response from the Power BI API. The embedded report shows updated data after the refresh completes (typically 5-15 minutes).
Common use cases
Customer-facing analytics portal
Build a client portal where customers log in to view Power BI reports tailored to their account data. The Lovable app handles authentication, the Edge Function generates an embed token scoped to that customer's reports with row-level security applied, and the customer sees a polished interface with their Power BI analytics embedded — without needing their own Power BI subscription.
Build a customer analytics portal. After the user logs in, call the power-bi-embed Edge Function with their organization ID to get an embed token for their specific Power BI report. Display the embedded Power BI report in a full-width panel below a custom header showing the customer's name and logo. Add a date range selector above the report that passes filter parameters to the Power BI embed to update the report's date slicer.
Copy this prompt to try it in Lovable
Executive dashboard with embedded KPI reports
An executive dashboard in Lovable combines summary KPI cards (from Supabase data) with embedded Power BI reports for the detailed drill-down views that executives need. The Lovable UI handles the navigation and summary metrics; Power BI handles the complex visualizations and analysis that would take weeks to recreate natively.
Create an executive dashboard page with two sections: a top section with four KPI cards showing total revenue, active customers, churn rate, and NPS score (from Supabase), and a bottom section with an embedded Power BI report showing monthly trend analysis. Add a Refresh Report button that calls the power-bi-refresh Edge Function to trigger a Power BI dataset refresh, then shows a toast 'Report data is being updated — check back in 5 minutes'.
Copy this prompt to try it in Lovable
Operational tool that writes data and shows analytics
A Lovable app serves as the data entry interface for operational teams while Power BI provides the analytical view of that data. When a user submits a form in Lovable that saves data to Supabase, a Supabase Edge Function pushes the data to a Power BI dataset or triggers a dataset refresh. The embedded Power BI report updates within minutes to reflect the new data.
Build an inventory management page where users can update stock levels through a Lovable form that saves to Supabase. When a stock update is submitted, trigger the power-bi-refresh Edge Function to refresh the inventory Power BI dataset. Show the refreshed Power BI inventory analytics report embedded in the page below the data entry form.
Copy this prompt to try it in Lovable
Troubleshooting
Edge Function returns 'AADSTS700016: Application not found' or 'invalid_client' error from Azure
Cause: The AZURE_CLIENT_ID or AZURE_TENANT_ID is incorrect — either miscopied from the Azure portal, pointing to the wrong tenant, or the app registration was deleted. Application not found errors can also occur if the app registration is in a different Azure tenant than the one specified by AZURE_TENANT_ID.
Solution: Go to portal.azure.com → App Registrations → find your app. Confirm the Application (client) ID and Directory (tenant) ID exactly match AZURE_CLIENT_ID and AZURE_TENANT_ID in Cloud → Secrets. Both values are GUIDs in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Copy them fresh from the Azure portal and update the secrets.
Power BI GenerateToken API returns 403 Forbidden even though the Azure token was obtained successfully
Cause: The service principal does not have the required Power BI API permissions, the service principal has not been added as a Member or Admin of the Power BI workspace, or the 'Allow service principals to use Power BI APIs' tenant setting is not enabled in the Power BI Admin Portal.
Solution: Check three things in order: (1) In the Power BI Admin Portal → Tenant settings → Developer settings → 'Allow service principals to use Power BI APIs', confirm the setting is enabled and applies to your service principal. (2) In Power BI Service → your workspace → Settings → Access, confirm the service principal is listed as a Member or Admin. (3) In Azure portal → your app registration → API Permissions, confirm the Power BI Service 'Report.ReadAll' and 'Dataset.ReadAll' permissions are granted.
The embedded Power BI report shows a blank white area instead of the report content
Cause: The embed token has expired, the embedUrl is incorrect, or the Power BI JavaScript SDK failed to initialize. The report also shows blank if the Power BI service principal does not have access to the specific report or if the report's workspace has been moved.
Solution: Open browser developer tools and check the Console for Power BI SDK error messages. Common fixes: (1) If the embed token expired, the tokenExpired event should trigger a refresh — verify the event handler is wired correctly. (2) If the embedUrl contains 'localhost' or the wrong domain, the GenerateToken call may have used a test URL — re-fetch the embed URL from the Power BI reports endpoint. (3) If you see CORS errors pointing to Power BI APIs directly, the report is trying to call Power BI from the frontend without a token — verify the tokenType is set to models.TokenType.Embed.
Dataset refresh API returns 429 Too Many Requests or 'Refresh is not possible' error
Cause: Power BI datasets have a limit of 48 refresh operations per day (for Import mode datasets on Premium capacity). Exceeding this limit results in 429 responses. 'Refresh is not possible' can mean the dataset is in DirectQuery mode (which doesn't need manual refreshes) or the dataset is currently refreshing.
Solution: Add debouncing or rate limiting to the Refresh Data button in the Lovable UI — disable it for at least 30 minutes after each successful trigger. Check the current refresh history in Power BI Service → Datasets → your dataset → Refresh history to see if the limit has been reached. For frequently updated data, consider switching the Power BI dataset to DirectQuery or Composite mode so reports always show live data without manual refreshes.
Best practices
- Cache the Azure AD access token in the Edge Function for its 60-minute lifetime rather than making a new token request on every embed token generation call — this reduces Azure AD API calls and improves Edge Function response time significantly for high-traffic dashboards.
- Generate embed tokens with the minimum necessary access level — use 'View' for dashboards where users only need to read reports, and only use 'Edit' for internal tools where power users need to modify reports.
- Implement embed token refresh in the frontend using the Power BI SDK's tokenExpired event so reports do not go blank after 60 minutes — call the Edge Function to get a fresh embed token and use the report object's updateSettings() method to apply it without reloading the report.
- For multi-tenant applications where each customer should only see their own data, configure Row-Level Security in your Power BI datasets and pass the user's identity in the identities parameter of the GenerateToken call — this is the only secure way to ensure tenant data isolation in embedded reports.
- Never expose the Power BI embed token to the browser's local storage or cookies where it could be extracted and reused — the token should only live in React component state and be discarded when the component unmounts.
- Test Power BI embedding on your published Lovable URL rather than the preview iframe — the preview iframe may have different CORS behavior, and Power BI's rendering in iframes has specific cross-origin requirements that only affect the deployed production URL.
- Monitor your Power BI capacity usage in the Azure portal — embedding reports consumes Power BI capacity units, and unexpected traffic spikes can exhaust capacity and cause reports to load slowly or fail. Set up Azure monitoring alerts for capacity threshold breaches.
Alternatives
Choose Looker if your organization uses Google Cloud and needs a modern semantic layer approach — Looker Embedded has a similar token-based embedding pattern but with better LookML-based data modeling.
Choose Mixpanel if your analytics focus is product usage and user behavior rather than business intelligence — Mixpanel's embed approach is simpler and more suited to product analytics than enterprise BI.
Choose Google Analytics if you need website traffic and conversion analytics — the Google Analytics Data API is simpler to integrate via Edge Functions and provides web-specific insights that Power BI data models typically require additional setup to replicate.
Frequently asked questions
Do my users need Power BI Pro licenses to view embedded reports in Lovable?
No. The app-owns-data pattern used in this guide generates embed tokens using a service principal with Power BI Premium capacity or Power BI Embedded A SKU. This means the embedding application (your Lovable app backed by the Azure service principal) holds the license, and individual viewers do not need their own Power BI Pro licenses. This is the key advantage of app-owns-data over user-owns-data embedding — it allows embedding for anonymous or non-Power BI users.
What Power BI capacity or licensing is required for embedding?
App-owns-data embedding requires either Power BI Premium Per User (PPU) at $20/user/month, Power BI Premium P-SKU (starting at ~$4,000/month for P1), or Azure Power BI Embedded A-SKU (starting at ~$735/month for A1 in most regions). Power BI Pro alone ($10/user/month) does not support app-owns-data embedding. For development and testing, Power BI Premium Per User is the most cost-effective option. For production at scale, evaluate A1 vs P1 based on your expected concurrent user count.
Can I apply filters to the embedded Power BI report from my Lovable frontend?
Yes. The Power BI JavaScript SDK supports programmatic filtering using the report object's updateFilters() method. After the report renders, you can apply filters based on user selections in your Lovable UI — for example, a date picker in Lovable that filters the embedded report's date dimension. The component ref (reportRef in the code example) gives you access to the report object for programmatic interaction. Ask Lovable to add filter controls that call report.setFilters() with the appropriate Power BI filter model.
How long does a Power BI embed token last and how do I refresh it?
Power BI embed tokens expire after 60 minutes by default. The Power BI JavaScript SDK fires a tokenExpired event when the token is about to expire. Handle this event by calling the powerbi-embed Edge Function again to get a new embed token, then use the setAccessToken() method on the embedded report object to update the token without reloading the entire report. The PowerBIReport component code example in Step 4 shows this token refresh pattern using the tokenExpired event handler.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation