Full-stack dashboard template for enterprise reporting applications. React/TypeScript frontend on Azure Static Web Apps with serverless API backend, Entra ID authentication, and role-based access control.
This template provides a foundation for building internal dashboards that pull data from external APIs (like Freshservice, ServiceNow, Jira) and present it with filtering, sorting, and role-based views. Originally built to give leadership visibility into IT projects without requiring admin access to the source system.
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ EXTERNAL API USERS │
│ (Freshservice, (Entra ID Auth) │
│ ServiceNow, etc.) │ │
│ │ │ │
└──────────┼───────────────────────────────┼──────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ AZURE STATIC WEB APPS │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ React / TypeScript Frontend │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Auth │ │ Data │ │ Filter │ │ Export │ │ │
│ │ │ Guard │──▶│ Tables │──▶│ Panel │──▶│ Button │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Azure Functions API │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ /auth │ │ /projects│ │ /tickets │ │ /export │ │ │
│ │ │ /me │ │ │ │ │ │ │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │ │ │ │ │
│ │ │ └──────────────┴──────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Entra │ │ External │ │ │
│ │ │ ID │ │ API Client │ │ │
│ │ └──────────┘ └──────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────┐
│ Application Insights │
│ │
│ - Request metrics │
│ - Error tracking │
│ - User analytics │
│ - Custom events │
└──────────────────────────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ │ │ │ │ │
│ User │────▶│ Frontend │────▶│ API │────▶│ External │
│ Request │ │ (cached?) │ │ (cached?) │ │ API │
│ │ │ │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ React │ │ Redis/ │
│ Query │ │ Memory │
│ Cache │ │ Cache │
└─────────────┘ └─────────────┘
Authentication and Authorization
- Entra ID SSO via MSAL
- Role-based access control (Admin, Manager, Viewer)
- Route guards for protected pages
- Token refresh handling
Dashboard Components
- Data tables with sorting, filtering, pagination
- Summary cards with KPIs
- Date range pickers
- Export to CSV/Excel
API Layer
- Proxy to external APIs (hides credentials from frontend)
- Response caching to reduce API calls
- Rate limiting
- Error handling and retry logic
Monitoring
- Application Insights integration
- Custom event tracking
- Performance metrics
- Error alerting
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, TanStack Query, Tailwind CSS |
| Hosting | Azure Static Web Apps |
| API | Azure Functions (Node.js/TypeScript) |
| Auth | Entra ID, MSAL React |
| Caching | TanStack Query (frontend), in-memory (API) |
| CI/CD | GitHub Actions |
| Monitoring | Application Insights |
azure-react-dashboard/
├── src/
│ ├── components/
│ │ ├── ui/ # Reusable UI components
│ │ │ ├── DataTable.tsx
│ │ │ ├── FilterPanel.tsx
│ │ │ ├── DateRangePicker.tsx
│ │ │ └── ExportButton.tsx
│ │ ├── layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── AuthGuard.tsx
│ │ └── dashboard/
│ │ ├── ProjectsTable.tsx
│ │ ├── TicketsTable.tsx
│ │ └── SummaryCards.tsx
│ ├── hooks/
│ │ ├── useAuth.ts
│ │ ├── useProjects.ts
│ │ └── useTickets.ts
│ ├── lib/
│ │ ├── api.ts # API client
│ │ ├── auth.ts # MSAL configuration
│ │ └── utils.ts
│ ├── types/
│ │ └── index.ts
│ ├── App.tsx
│ └── main.tsx
├── api/
│ ├── src/
│ │ ├── functions/
│ │ │ ├── projects.ts
│ │ │ ├── tickets.ts
│ │ │ └── export.ts
│ │ ├── lib/
│ │ │ ├── freshservice.ts # External API client
│ │ │ ├── cache.ts
│ │ │ └── auth.ts
│ │ └── middleware/
│ │ └── validateToken.ts
│ ├── host.json
│ └── package.json
├── infrastructure/
│ ├── main.bicep
│ └── parameters.json
├── .github/
│ └── workflows/
│ ├── deploy.yml
│ └── pr-check.yml
└── README.md
Environment Variables (Frontend)
VITE_API_URL=/api
VITE_AZURE_CLIENT_ID=<your-app-registration-client-id>
VITE_AZURE_TENANT_ID=<your-tenant-id>
Environment Variables (API)
FRESHSERVICE_API_KEY=<api-key>
FRESHSERVICE_DOMAIN=<your-domain>.freshservice.com
AZURE_CLIENT_ID=<same-as-frontend>
AZURE_TENANT_ID=<your-tenant-id>
APPINSIGHTS_CONNECTION_STRING=<connection-string>
Roles are managed via Entra ID groups. The API validates group membership on each request.
| Role | Permissions |
|---|---|
| Viewer | Read-only access to dashboards |
| Manager | Viewer + export capabilities |
| Admin | Manager + configuration access |
Implementation in the API:
// middleware/validateToken.ts
const allowedRoles = ['Dashboard.Viewer', 'Dashboard.Manager', 'Dashboard.Admin'];
const userRoles = decodedToken.roles || [];
const hasAccess = userRoles.some(role => allowedRoles.includes(role));Frontend (TanStack Query)
- Stale time: 5 minutes
- Cache time: 30 minutes
- Background refetch on window focus
API (In-Memory)
- TTL: 5 minutes for list endpoints
- TTL: 1 minute for detail endpoints
- Cache invalidation on data mutation
This reduces calls to the external API while keeping data reasonably fresh. For a dashboard viewed by 25+ users, this prevents rate limiting issues.
Automated via GitHub Actions:
-
PR Check (
pr-check.yml)- Lint and type check
- Unit tests
- Build verification
-
Deploy (
deploy.yml)- Triggers on push to
main - Builds frontend and API
- Deploys to Azure Static Web Apps
- Runs smoke tests
- Triggers on push to
Why Azure Static Web Apps?
Combines React hosting and Functions API in a single resource. Built-in auth integration with Entra ID, automatic HTTPS, and global CDN. Eliminates the need to manage CORS between separate frontend and API deployments.
Why proxy external APIs through Functions?
- Keeps API keys server-side (not exposed in browser)
- Enables caching to reduce external API calls
- Provides a consistent error handling layer
- Allows adding custom business logic (filtering, aggregation)
Why TanStack Query?
Handles caching, background refetching, loading/error states, and request deduplication out of the box. Significantly reduces boilerplate compared to manual fetch + useState.
After deploying this dashboard:
- Eliminated 4+ hours/week of manual report generation
- 25+ daily active users across leadership and project managers
- 99.9% uptime over 6 months
- Average page load under 1.5 seconds
- Azure AI Registry - Similar architecture, different use case
- GitHub Actions for Azure - CI/CD workflows