Metabase Local Testing Guide
This guide will help you test the Metabase integration locally before deploying to staging/production.
Prerequisites
- Docker and Docker Compose installed
- Backend running locally (port 4000)
- Frontend PWA or Web App running locally
- A valid Firebase authentication token for API requests
Step 1: Start Metabase Locally
Metabase is already configured in docker-compose.yml. Start it:
# Start Metabase (and dependencies)
docker compose up metabase -d
# Check if Metabase is running
docker compose ps metabase
# View Metabase logs
docker compose logs -f metabase
Metabase will be available at: http://localhost:3002
Note: The first startup may take 1-2 minutes. Wait until you see "Metabase initialization complete" in the logs.
Step 2: Configure Metabase
2.1 Initial Setup
- Open http://localhost:3002 in your browser
- Complete the initial setup wizard:
- Create an admin account (remember these credentials!)
- Configure your organization details
- Skip database connection for now (we'll add it later)
2.2 Enable Static Embedding (FREE)
Important: We're using Metabase Open Source (self-hosted), which includes Static Embedding for FREE. You may see a "Embedded Analytics" tab that shows pricing - ignore that, we only need the free "Static Embedding" feature.
Step-by-step:
-
Navigate to Settings → Admin → Embedding
- You'll see two tabs: "Static embedding" and "Embedded Analytics"
-
Click on the "Static embedding" tab
- ⚠️ Don't click "Embedded Analytics" - that's the paid feature ($575/month)
- If you accidentally click it and see a pricing page, just go back to "Static embedding"
-
Toggle the switch "Enable static embedding" to ON
- The switch should turn blue/green when enabled
-
A Secret key will appear (long alphanumeric string)
- Example format:
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 - Copy this key immediately - you'll need it for your backend configuration
- Store it securely - you'll add it to your
.envfile in the next step
- Example format:
-
Verify the key is copied
- The secret key is used to sign JWT tokens for embedding
- It must match exactly between Metabase and your backend
What you should see:
- ✅ "Static embedding" tab is active
- ✅ "Enable static embedding" toggle is ON
- ✅ A long secret key is displayed
- ❌ No pricing information or payment prompts
Troubleshooting:
- If you don't see a "Static embedding" tab, make sure you're using Metabase Open Source (not Cloud)
- If the toggle won't enable, refresh the page and try again
- If you see a pricing page, you clicked the wrong tab - go back to "Static embedding"
2.3 Connect to Your Local Database (Optional)
If you want to test with real data:
- Go to Settings → Admin → Databases → Add Database
- Select PostgreSQL
- Configure connection:
- Name:
FlowPOS Local DB - Host:
postgres_dev(Docker service name) orlocalhost - Port:
5432(or5435if connecting from host) - Database name:
flowpos_dev - Username:
flowpos - Password:
flowpos - Additional JDBC connection string options:
?sslmode=disable
- Name:
- Click Save
Step 3: Configure Backend Environment Variables
Add these environment variables to your .env file (or export them):
# Metabase Configuration
METABASE_SITE_URL=http://localhost:3002
METABASE_EMBED_SECRET_KEY=<your-embedding-secret-key-from-step-2.2>
Important: The METABASE_EMBED_SECRET_KEY must match the secret key from Metabase's embedding settings.
Example .env addition
# Metabase Local Testing
METABASE_SITE_URL=http://localhost:3002
METABASE_EMBED_SECRET_KEY=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
Step 4: Restart Backend
Restart your backend to load the new environment variables:
# If using Docker
docker compose restart backend
# If running directly
cd apps/backend
pnpm start:dev
Step 5: Create a Test Dashboard in Metabase
5.1 Create a Test SQL Question
Option A: Using Native Query (Recommended for Testing)
- In Metabase, click the "+" button in the top navigation (or go to Browse Data)
- Select "Native query" (or "SQL query")
- If you don't see this option, try: "New" → "Question" → "Native query"
- In the SQL editor, paste this simple test query:
SELECT
CAST({{business_id}} AS TEXT) AS business_id,
CURRENT_DATE AS test_date,
'Test Dashboard' AS message
Important:
{{business_id}}is NOT inside quotes - parameters cannot be used inside string literals- We use
CAST({{business_id}} AS TEXT)to ensure it's treated as text (since it's a UUID) - If you get an error "parameters cannot be used in comments or as identifiers", check that the parameter is not inside quotes
-
Add a variable:
- Look for a "Variables" button or section (usually below the SQL editor)
- Click "Add a variable" or the "+" button
- Variable name:
business_id - Variable type: Text
- Click "Add" or "Done"
-
Click "Run" to test the query (it will prompt for
business_id- enter any test UUID like550e8400-e29b-41d4-a716-446655440000)
Note: If you get an error about "parameters cannot be used in comments or as identifiers", make sure:
- The parameter
{{business_id}}is NOT inside quotes - The parameter is used as a value, not as a column name or identifier
- Click "Save" → "Save as question"
- Name it:
Test Business Query - Choose a collection (or create a new one)
- Click "Save"
- Name it:
Alternative: If you can't find Native Query
If the navigation is different in your Metabase version:
- Click "New" or "+" button
- Select "Question"
- Choose "Native query" or "SQL"
- Follow steps 3-6 above
Note: The exact navigation may vary slightly depending on your Metabase version, but look for options like "New", "Question", "Native query", or "SQL query".
5.2 Create a Dashboard
- Go to Dashboards → New Dashboard
- Name it:
Test Dashboard - Click Add a question → Select your test question
- Save the dashboard
- Note the Dashboard ID from the URL (e.g.,
http://localhost:3002/dashboard/1→ ID is1)
5.3 Configure Embedding
- In your dashboard, click ⋮ (three dots) → Embed this dashboard
- Enable embedding for this dashboard
- Configure parameters:
- Find
business_idparameter - Mark it as Locked (users can't change it)
- Mark it as Hidden (users can't see it)
- Find
- Click Save
- Copy the Dashboard ID (you'll need it for testing)
Step 6: Test Backend Endpoint
6.1 Get a Firebase Auth Token
You'll need a valid Firebase ID token. You can get this from:
- Your frontend app (check browser DevTools → Application → Cookies)
- Or use your existing authentication flow
6.2 Test with cURL
# Replace <YOUR_TOKEN> with your Firebase ID token
# Replace <DASHBOARD_ID> with your dashboard ID (e.g., 1)
# Replace <BUSINESS_ID> with a test business ID (UUID format)
curl -X GET "http://localhost:4000/reports/dashboard/1/embed-url?businessId=550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-H "Content-Type: application/json"
Expected Response:
{
"embedUrl": "http://localhost:3002/embed/dashboard/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...?bordered=true&titled=true"
}
6.3 Test Error Cases
# Missing businessId (should fail)
curl -X GET "http://localhost:4000/reports/dashboard/1/embed-url" \
-H "Authorization: Bearer <YOUR_TOKEN>"
# Invalid dashboardId (should fail)
curl -X GET "http://localhost:4000/reports/dashboard/invalid/embed-url?businessId=550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer <YOUR_TOKEN>"
Step 7: Test Frontend Components
7.1 Test PWA Component
-
Start your PWA frontend:
cd apps/frontend-pwa
pnpm dev -
Create a test page or add to an existing page:
import { MetabaseDashboardFrame } from "@/features/reports/MetabaseDashboardFrame";
export function TestReportsPage() {
// Replace 1 with your actual dashboard ID
return (
<div className="p-4">
<h1>Test Metabase Dashboard</h1>
<MetabaseDashboardFrame
dashboardId={1}
height="600px"
/>
</div>
);
}
- Navigate to the page and verify:
- Loading state appears initially
- Dashboard loads in iframe
- No console errors
7.2 Test Web App Component
-
Start your Web App:
cd apps/web-app
pnpm dev -
Create a test page (e.g.,
app/(private)/business/[businessId]/reports/page.tsx):
"use client";
import { MetabaseDashboardFrame } from "@/features/reports/MetabaseDashboardFrame";
export default function ReportsPage() {
// Replace 1 with your actual dashboard ID
return (
<div className="p-4">
<h1>Test Metabase Dashboard</h1>
<MetabaseDashboardFrame
dashboardId={1}
height="600px"
/>
</div>
);
}
- Navigate to the page and verify the dashboard loads
Step 8: Verify JWT Token
You can decode the JWT token from the embed URL to verify it contains the correct business_id:
-
Copy the embed URL from the backend response
-
Extract the JWT token (the part after
/embed/dashboard/) -
Decode it at https://jwt.io
-
Verify the payload contains:
{
"resource": { "dashboard": 1 },
"params": {
"business_id": "550e8400-e29b-41d4-a716-446655440000"
},
"exp": 1234567890
}
Troubleshooting
Backend Errors
Error: "METABASE_SITE_URL is not configured"
- Solution: Add
METABASE_SITE_URLto your.envfile
Error: "METABASE_EMBED_SECRET_KEY is not configured"
- Solution: Add
METABASE_EMBED_SECRET_KEYto your.envfile (must match Metabase embedding secret)
Error: "businessId is required"
- Solution: Make sure you're passing
businessIdas a query parameter
Frontend Errors
Error: "Failed to load dashboard"
- Check browser console for detailed error
- Verify backend is running and accessible
- Verify Firebase token is valid
- Check network tab for API request/response
Dashboard shows "Invalid token"
- Verify
METABASE_EMBED_SECRET_KEYmatches Metabase embedding secret - Check that embedding is enabled in Metabase
- Verify dashboard ID is correct
Dashboard loads but shows no data
- Verify
business_idparameter is configured in Metabase question - Check that the question includes
WHERE business_id = {{business_id}} - Verify the
business_idvalue exists in your database
Metabase Issues
Metabase won't start
- Check Docker logs:
docker compose logs metabase - Verify port 3002 is not in use
- Try restarting:
docker compose restart metabase
Can't access Metabase UI
- Verify Metabase is running:
docker compose ps metabase - Check if port 3002 is accessible:
curl http://localhost:3002/api/health - Try accessing via
http://127.0.0.1:3002instead oflocalhost
Next Steps
Once local testing is successful:
- ✅ Backend endpoint returns valid embed URLs
- ✅ Frontend components load dashboards correctly
- ✅ JWT tokens contain correct
business_id - ✅ Data isolation works (different
business_idvalues show different data)
You're ready to proceed with:
- Phase 3: Secrets & Configuration (add secrets to Doppler)
- Phase 4: Deployment (deploy to staging/production)