This directory contains the API service for the MyToDoApp application, implemented using Microsoft Data API Builder (DAB). The API service provides REST and GraphQL endpoints to access the Azure SQL Database using Azure AD authentication and Managed Identity.
The API service is automatically deployed by Azure Developer CLI (azd) as a separate container app alongside the main web application. It provides secure, authenticated access to the todo database through modern API standards.
- REST API: RESTful endpoints at
/apifor CRUD operations - GraphQL API: GraphQL endpoint at
/graphqlfor flexible queries - Azure AD Authentication: JWT-based authentication using Azure AD
- Managed Identity: Passwordless database access using Azure Managed Identity
- Application Insights: Telemetry and monitoring integration
- CORS Support: Configurable cross-origin resource sharing
The API service is defined in azure.yaml as a separate service:
services:
api:
project: ./api
host: containerapp
language: python
docker:
path: dockerfile
remoteBuild: true- Build Phase:
azd deploybuilds the Docker image using thedockerfilein this directory - Remote Build: The container is built in Azure Container Registry (ACR) with
remoteBuild: true - Container Deployment: The built image is deployed to Azure Container Apps
- Environment Variables:
azdinjects required environment variables from the azd environment - Startup: The
entrypoint.shscript waits for Managed Identity token availability before starting DAB
api/
├── README.md # This file
├── dockerfile # Multi-stage Docker build for Data API Builder
├── entrypoint.sh # Startup script with MI token wait logic
└── dab-config.json # Data API Builder configuration
Purpose: Multi-stage Docker build that installs Data API Builder and configures the runtime environment.
Build Stages:
-
Build Stage (
mcr.microsoft.com/dotnet/sdk:8.0):- Creates a dotnet tool manifest
- Installs
Microsoft.DataApiBuilderas a dotnet tool - Copies the pre-configured
dab-config.json
-
Runtime Stage (
mcr.microsoft.com/azure-databases/data-api-builder):- Copies the DAB installation and config from build stage
- Adds the
entrypoint.shwrapper script - Sets environment variables for Managed Identity wait behavior
Environment Variables:
| Variable | Default | Description |
|---|---|---|
MI_RESOURCE |
https://database.windows.net |
Azure resource URL for token acquisition |
MI_INITIAL_DELAY_SECONDS |
0 |
Initial delay before checking MI token availability |
MI_PERIOD_SECONDS |
1 |
Wait period between token availability checks |
MI_FAILURE_THRESHOLD |
60 |
Maximum number of retry attempts before failing |
Purpose: Wrapper script that ensures Managed Identity tokens are available before starting Data API Builder.
Functionality:
- Environment Detection: Checks if running in Azure Container Apps by detecting
IDENTITY_ENDPOINT - Managed Identity Wait Loop:
- Polls the Managed Identity endpoint until HTTP 200 is received
- Uses configurable retry logic with exponential backoff capabilities
- Supports user-assigned managed identity via
MI_CLIENT_IDorAZURE_CLIENT_ID
- Startup: Launches Data API Builder with the configuration file once MI is ready
- Logging: Provides timestamped logs for troubleshooting startup issues
Why This Is Needed:
Azure Container Apps may start containers before the Managed Identity system is fully initialized. Without this wait logic, DAB might fail to acquire database tokens on first startup, causing authentication errors.
Configuration Parameters:
- MI_RESOURCE: The Azure resource for which to acquire tokens (default: SQL Database)
- MI_INITIAL_DELAY_SECONDS: Grace period before first check (useful for cold starts)
- MI_PERIOD_SECONDS: How often to retry token acquisition
- MI_FAILURE_THRESHOLD: Maximum retries before giving up (prevents infinite loops)
- MI_CLIENT_ID: Optional user-assigned managed identity client ID
Skip Behavior:
If IDENTITY_ENDPOINT is not set (e.g., running locally), the wait logic is skipped and DAB starts immediately.
Purpose: Configuration file for Data API Builder that defines database connection, authentication, and entity mappings.
Configuration Sections:
"data-source": {
"database-type": "mssql",
"connection-string": "@env('DATABASE_CONNECTION_STRING')"
}- Database Type: Microsoft SQL Server (
mssql) - Connection String: Loaded from
DATABASE_CONNECTION_STRINGenvironment variable - Authentication: Connection string includes Managed Identity authentication
REST API:
- Enabled at path
/api - Provides RESTful endpoints for CRUD operations
- Automatic HTTP verb mapping (GET, POST, PUT, PATCH, DELETE)
GraphQL API:
- Enabled at path
/graphql - Supports flexible query and mutation capabilities
- Supports nested queries and filtering
CORS:
- Configured to accept requests only from the deployed frontend application
- Uses
@env('APP_URL')environment variable to dynamically restrict origins allow-credentialsset totrueto support authenticated requests with cookies- The
APP_URLis set during post-deployment by thepostdeploy.ps1script
Authentication:
- Provider: Azure AD (
AzureAD) - Audience: API application ID URI from
API_APP_ID_URIenvironment variable - Issuer: Azure AD tenant-specific issuer URL using
TENANT_ID - JWT Validation: Automatic token validation for all requests
"telemetry": {
"application-insights": {
"enabled": true,
"connection-string": "@env('APPLICATIONINSIGHTS_CONNECTION_STRING')"
}
}Integrates with Azure Application Insights for:
- Request/response logging
- Performance metrics
- Exception tracking
- Custom telemetry
"entities": {
"todo": {
"source": "dbo.ToDo",
"permissions": [
{
"role": "authenticated",
"actions": ["*"]
}
]
}
}Entity Mapping:
- Entity Name:
todo(API endpoint name) - Database Source:
dbo.ToDotable - Permissions: Authenticated users can perform all actions (Create, Read, Update, Delete)
Generated Endpoints:
REST:
GET /api/todo- List all todosGET /api/todo/{id}- Get todo by IDPOST /api/todo- Create new todoPUT /api/todo/{id}- Update todoPATCH /api/todo/{id}- Partial updateDELETE /api/todo/{id}- Delete todo
GraphQL:
POST /graphql- GraphQL queries and mutations
The API service requires the following environment variables to be set by azd during deployment:
| Variable | Description | Set By |
|---|---|---|
DATABASE_CONNECTION_STRING |
SQL connection string with Managed Identity auth | azd (from infra outputs) |
API_APP_ID_URI |
Azure AD API identifier URI (e.g., api://guid) |
preup.ps1 script |
TENANT_ID |
Azure AD tenant ID | preup.ps1 script |
APPLICATIONINSIGHTS_CONNECTION_STRING |
Application Insights connection string | azd (from infra outputs) |
AZURE_CLIENT_ID |
User-assigned managed identity client ID | azd (from infra outputs) |
APP_URL |
Frontend application URL for CORS restrictions | postdeploy.ps1 script |
The API service connects to Azure SQL Database using the user-assigned managed identity. The required permissions are configured by the postprovision.ps1 script:
- db_datareader: Read access to all tables
- db_datawriter: Write access to all tables
- db_ddladmin: Schema modification permissions (if needed)
These permissions allow DAB to perform all CRUD operations on the dbo.ToDo table on behalf of authenticated users.
For more details on the provisioning process, see the Scripts Documentation.
- Client Request: Frontend sends request with Azure AD JWT token in
Authorizationheader - Token Validation: DAB validates the JWT token against Azure AD
- Role Check: Verifies user has
authenticatedrole (any valid Azure AD user) - Database Access: DAB uses Managed Identity to query database
- Response: Returns data to authenticated client
- The API service uses passwordless authentication via Managed Identity
- No SQL credentials stored in code, configuration, or environment variables
- Connection string specifies
Authentication=Active Directory Default
To run the API service locally:
-
Prerequisites:
- Docker installed
- Azure CLI logged in (
az login) - Environment variables configured
-
Build the container:
docker build -t todo-api ./api
-
Run with environment variables:
docker run -p 5000:5000 \ -e DATABASE_CONNECTION_STRING="Server=..." \ -e API_APP_ID_URI="api://your-guid" \ -e TENANT_ID="your-tenant-id" \ -e APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=..." \ todo-api
-
Test the API:
- REST:
curl http://localhost:5000/api/todo - GraphQL: Access GraphQL playground at
http://localhost:5000/graphql
- REST:
Symptom: Container exits shortly after starting
Possible Causes:
- Managed Identity token not available (check
MI_FAILURE_THRESHOLD) - Missing environment variables (check container logs)
- Invalid
dab-config.json(validate JSON syntax)
Solution:
Check container logs in Azure Portal for detailed error messages
Symptom: API returns 401 Unauthorized
Possible Causes:
- JWT token missing or expired
- Incorrect
API_APP_ID_URIorTENANT_ID - Token audience doesn't match configuration
Solution:
Verify Azure AD app registration and environment variables
Symptom: API returns 500 errors or database timeout
Possible Causes:
- Managed identity not granted database permissions
- SQL firewall blocking Container Apps IP range
- Connection string incorrect
Solution:
- Run
postprovision.ps1to ensure permissions are set - Verify SQL firewall allows Azure services (configured in infrastructure)
- Check
DATABASE_CONNECTION_STRINGformat
Symptom: Container logs show "Timed out waiting for Managed Identity token"
Solution: Increase MI_FAILURE_THRESHOLD environment variable in dockerfile or deployment configuration
The API service sends telemetry to Application Insights. Monitor these metrics:
- Request Rate: Number of API calls per minute
- Response Time: Average latency of API requests
- Error Rate: Percentage of failed requests (4xx/5xx)
- Database Query Time: Time spent in SQL queries
- Authentication Failures: Failed JWT validation attempts
Access metrics in Azure Portal → Application Insights → Application Map/Performance/Failures
- Scripts Documentation - azd lifecycle scripts including
preup.ps1andpostprovision.ps1 - Infrastructure Documentation - Bicep modules and Azure resource configuration