Loading repository data…
Loading repository data…
olliverc1985 / repository
Production-ready Azure Static Web Apps template with React, Azure Functions, Cosmos DB, and full CI/CD. Includes auth, invoicing, client management, and 198 tests.
A comprehensive Azure Static Web Apps template with React, Azure Functions, Cosmos DB, and full CI/CD.
A feature-complete starter template for building modern fullstack applications on Azure. Includes authentication, database integration, invoice generation, client management, and more—all configured and ready to deploy.
📋 Note: This template is configured for ease of development and learning. See the Security Considerations section for production hardening recommendations.
Want to see the app in action? Use GitHub Codespaces for a one-click demo environment with pre-populated data.
When you open in Codespaces, the environment automatically:
After the Codespace opens, run this command to start everything:
docker rm -f cosmos 2>/dev/null; docker run -d --name cosmos -p 8081:8081 -p 1234:1234 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview --protocol https && echo "⏳ Waiting 90s for Cosmos DB..." && sleep 90 && cd /workspaces/azure-swa-fullstack-starter/app/api && NODE_PATH=./node_modules NODE_TLS_REJECT_UNAUTHORIZED=0 node ../../scripts/seed-demo-data.js && cd .. && npm run build && npm run build:api && swa start dist --api-location api
This will:
Click the forwarded port 4280 link to open the app.
| Account | Password | |
|---|---|---|
| Admin | demo@example.com | Demo123! |
| Worker | sarah.wilson@example.com | Worker123! |
The seed script creates a full year of realistic data (2025 + January 2026):
# Seed demo data (run from app/api directory)
cd /workspaces/azure-swa-fullstack-starter/app/api && NODE_PATH=./node_modules NODE_TLS_REJECT_UNAUTHORIZED=0 node ../../scripts/seed-demo-data.js
# Reset and re-seed all data
cd /workspaces/azure-swa-fullstack-starter/app/api && NODE_PATH=./node_modules NODE_TLS_REJECT_UNAUTHORIZED=0 node ../../scripts/seed-demo-data.js --reset
# Build and run (after seeding)
cd /workspaces/azure-swa-fullstack-starter/app && npm run build && npm run build:api && swa start dist --api-location api
Note: Codespaces offers 60 free hours/month on 2-core machines. The Cosmos DB emulator runs in Docker within the Codespace, so it works on any machine including Apple Silicon Macs.
git clone https://github.com/your-username/azure-swa-fullstack-starter.git
cd azure-swa-fullstack-starter/app
# Install frontend dependencies
npm install
# Install API dependencies
cd api && npm install && cd ..
# Copy environment template
cp ../.env.example .env
# Edit .env with your settings
Create api/local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "node",
"COSMOS_DB_CONNECTION_STRING": "your-cosmos-connection-string",
"COSMOS_DB_DATABASE_NAME": "appdb",
"JWT_SECRET": "your-development-secret-min-32-chars"
}
}
# Terminal 1 - Frontend
npm run dev
# Terminal 2 - API
npm run dev:api
# Or run both with SWA CLI
npm run dev:swa
COSMOS_DB_CONNECTION_STRING="your-connection-string" \
node ../scripts/create-admin-user.js admin@example.com YourSecurePass123!
Update .env with your app details:
VITE_APP_NAME="My App"
VITE_APP_DESCRIPTION="My awesome application"
VITE_APP_TAGLINE="Welcome to My App"
VITE_LOGO_PATH="/logo.svg"
VITE_OWNER_NAME="Your Name"
VITE_OWNER_COMPANY="Your Company"
VITE_SHOW_OWNER_FOOTER="true"
Edit app/tailwind.config.js to customise the primary colour palette:
primary: {
50: '#your-lightest',
500: '#your-primary', // Main brand colour
900: '#your-darkest',
}
Edit infrastructure/bicep/main.bicep:
var appPrefix = 'yourapp' // Change this to your app name
🔒 Production: Before deploying to production, review the Security Considerations section for recommended hardening steps.
azure-swa-fullstack-starter/
├── app/ # Main application
│ ├── src/ # React frontend
│ │ ├── components/ # UI components
│ │ ├── pages/ # Page components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── services/ # API services
│ │ ├── config/ # App configuration
│ │ └── types/ # TypeScript types
│ ├── api/ # Azure Functions API
│ │ └── src/
│ │ ├── functions/ # Function handlers
│ │ └── shared/ # Shared utilities
│ └── public/ # Static assets
├── infrastructure/ # Infrastructure as Code
│ └── bicep/ # Bicep templates
├── scripts/ # Utility scripts
├── docs/ # Documentation
└── .github/workflows/ # CI/CD pipelines
| Feature | Admin | Worker |
|---|---|---|
| View dashboard | ✅ | ❌ |
| Take register | ✅ | ✅ |
| View/edit clients | ✅ | ✅ |
| Add new clients | ✅ | ✅ |
| View payments | ✅ | ❌ |
| Generate invoices | ✅ | ❌ |
| Manage users | ✅ | ❌ |
| Manage settings | ✅ | ❌ |
This repo includes a working CI pipeline (.github/workflows/ci.yml) that runs automatically:
To deploy to Azure, you'll need to set up the deployment workflow:
Rename the template workflow
mv .github/workflows/deploy.yml.example .github/workflows/deploy.yml
Create Resource Group
az group create --name rg-myapp-dev --location westeurope
Deploy Infrastructure
az deployment group create \
--resource-group rg-myapp-dev \
--template-file ./infrastructure/bicep/main.bicep \
--parameters environment=dev
Configure GitHub Secrets
AZURE_CREDENTIALS - Service principal JSON (how to create)AZURE_STATIC_WEB_APPS_API_TOKEN - From Azure Portal > Static Web App > Manage deployment tokenJWT_SECRET - Secret for JWT signing (min 32 characters)Update resource names in deploy.yml (search for yourapp)
Deploy - Push to main branch to trigger deployment
cd app
npm run build
npm run build:api
swa deploy --env production
⚠️ Important: This template prioritises ease of setup for development and learning. For production deployments, review the security measures below.
| Component | Current Configuration | Why |
|---|---|---|
| Cosmos DB | Public network access enabled | Simplifies local development and initial setup |
| Cosmos DB Auth | Connection string-based | Works out of the box without identity configuration |
| Key Vault | Access policies (not RBAC) | Simpler initial configuration |
Azure Static Web Apps managed Functions (the /api folder) have inherent platform limitations that affect security architecture:
| Feature | Managed Functions | Bring Your Own Functions |
|---|---|---|
| Managed Identity | ❌ | ✅ |
| VNet Integration (outbound) | ❌ | ✅ |
| Private Endpoints to backends | ❌ | ✅ |
| Key Vault References | ❌ | ✅ |
Note: The SWA "Private Endpoint" feature is for inbound traffic only (restricting access to your static web app). It does not provide outbound connectivity from managed Functions to private backend services.
This means managed Functions cannot connect to Cosmos DB via Private Endpoints. This is a known Azure platform limitation.
If you stay with managed Functions, apply these security measures:
// In infrastructure/bicep/main.bicep - Add IP restrictions to Cosmos DB
resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
name: cosmosDbName
location: location
properties: {
// ... existing properties ...
// Allow only Azure services and specific IPs
publicNetworkAccess: 'Enabled'
ipRules: [
{ ipAddressOrRange: 'your-office-ip' } // Add known IPs
]
networkAclBypass: 'AzureServices' // Allow Azure services including SWA
minimalTlsVersion: 'Tls12'
}
}
Additional hardening:
enableRbacAuthorization: true)For production workloads requiring Private Endpoints and managed identity, migrate to Bring Your Own Functions:
Create a separate Azure Functions app with:
Link to your Static Web App:
az staticwebapp functions link \
--name <swa-name> \
--resource-group <resource-group> \
--function-resource-id <functions-app-resource-id>
Configure Cosmos DB with Private Endpoint:
resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
properties: {
publicNetworkAccess: 'Disabled'