API Builder Runtime
The API Builder Runtime is the execution engine that hosts and serves your configured APIs. It's implemented as an ASP.NET Core application that reads your API Builder configuration from Dataverse and exposes it as REST endpoints.
Runtime Architecture
┌───────────────────────────────────────────────────────────────────────┐
│ API Builder RUNTIME ARCHITECTURE │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────┐ │
│ │ Client │ │ API Builder Runtime │ │
│ │ Application │ │ │ │
│ └──────┬──────┘ │ ┌─────────────────────────────┐ │ │
│ │ │ │ ASP.NET Core Host │ │ │
│ │ HTTP Request │ │ │ │ │
│ ▼ │ │ ┌───────────────────────┐ │ │ │
│ ┌─────────────┐ │ │ │ REST Server │ │ │ │
│ │ Gateway │───────►│ │ │ (Flowon.Xrm.Api) │ │ │ │
│ │ (Optional) │ │ │ └───────────┬───────────┘ │ │ │
│ └─────────────┘ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌───────────────────────┐ │ │ │
│ │ │ │ Metadata Cache │ │ │ │
│ │ │ │ (API Configuration) │ │ │ │
│ │ │ └───────────┬───────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └──────────────┼──────────────┘ │ │
│ │ │ │ │
│ └─────────────────┼───────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Dataverse / Dataverse │ │
│ │ │ │
│ │ ┌───────────┐ ┌─────────────┐ │ │
│ │ │ Flowon │ │ Business │ │ │
│ │ │ Config │ │ Data │ │ │
│ │ └───────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
Hosting Project Structure
A API Builder Runtime project is a standard ASP.NET Core application with the Flowon packages installed.
Required NuGet Packages
<PackageReference Include="Flowon.Xrm.Api.AspNetCore" Version="x.x.x" />
Project Structure
MyCompany.Api/
├── Program.cs # Application entry point
├── Bootstrapper.cs # Service configuration
├── UserResolver.cs # Custom user resolution
├── appsettings.json # Base configuration
├── appsettings.Development.json # Development overrides
└── appsettings.ext.json # External configuration (secrets)
Bootstrapper Configuration
The Bootstrapper class configures all services required for the API Builder Runtime:
using Flowon.Xrm.Api.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Authentication.JwtBearer;
public static class Bootstrapper
{
public static void Run(string apiDisplayName, string projectName, string apiName)
{
var builder = WebApplication.CreateBuilder();
// Load configuration from multiple sources
builder.Configuration
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.ext.json", optional: true);
builder.Services.AddControllers();
// Configure JWT authentication
ConfigureAuthentication(builder);
// Get Dataverse connection string
var dataverseConnectionString = builder.Configuration["Dynamics365ConnectionString"];
// Configure API Builder Runtime
var flowonApiBuilder = builder.Services
.AddFlowonApi(
projectName,
apiName,
sp => new ResilientServiceClient(dataverseConnectionString))
.AddRestServer(o =>
{
if (builder.Environment.IsDevelopment())
{
o.VersioningStrategy = new DraftVersionApiVersioningStrategy();
}
else
{
o.VersioningStrategy = new LatestVersionApiVersioningStrategy();
}
})
.AddUserResolver<UserResolver>();
// Enable metadata auto-refresh in development
if (builder.Environment.IsDevelopment())
{
flowonApiBuilder.AddMetadataWatcher(c => c.TimeIntervalInMinutes = 3);
}
// Build and configure the application
var app = builder.Build();
app
.UseHttpsRedirection()
.UseCors("CorsPolicy")
.UseAuthentication()
.UseRouting()
.UseAuthorization()
.UseEndpoints(endpoints =>
{
endpoints.UseRestServer();
});
app.Run();
}
}
Key Configuration Components
AddFlowonApi
The AddFlowonApi method registers the API Builder services:
builder.Services.AddFlowonApi(
projectName, // Flowon Project name
apiName, // API Builder name
serviceClientFactory // Factory for Dataverse connection
);
| Parameter | Description |
|---|---|
| projectName | The name of the Flowon Project containing the API |
| apiName | The name of the API Builder to host |
| serviceClientFactory | Factory function that creates the Dataverse service client |
AddRestServer
The AddRestServer method configures the REST server:
.AddRestServer(options =>
{
options.VersioningStrategy = new LatestVersionApiVersioningStrategy();
});
Versioning Strategies
| Strategy | Description | Use Case |
|---|---|---|
| DraftVersionApiVersioningStrategy | Uses the Draft version | Development and testing |
| LatestVersionApiVersioningStrategy | Uses the latest published version | Production deployments |
| SpecificVersionApiVersioningStrategy | Uses a specific version number | Pinned deployments |
AddUserResolver
The AddUserResolver method registers a custom user resolver:
.AddUserResolver<UserResolver>();
The user resolver maps the authenticated user (from JWT) to a Dataverse user:
public class UserResolver : IUserResolver
{
public async Task<UserContext> ResolveAsync(ClaimsPrincipal principal)
{
// Extract user information from JWT claims
var userId = principal.FindFirst("sub")?.Value;
var email = principal.FindFirst("email")?.Value;
// Return user context for Dataverse operations
return new UserContext
{
UserId = Guid.Parse(userId),
Email = email,
Roles = ExtractRoles(principal)
};
}
}
AddMetadataWatcher
The AddMetadataWatcher enables automatic metadata refresh:
.AddMetadataWatcher(config =>
{
config.TimeIntervalInMinutes = 3; // Check for changes every 3 minutes
});
This is useful during development to pick up configuration changes without restarting the application.
Application Configuration
appsettings.json
{
"FLOWON_PROJECT_NAME": "CustomerPortal",
"FLOWON_API_NAME": "CustomerPortalAPI",
"FLOWON_API_DISPLAY_NAME": "Customer Portal API",
"FLOWON_URL_PATH": "/customer-portal",
"Dynamics365ConnectionString": "AuthType=ClientSecret;Url=https://org.crm.dynamics.com;ClientId=xxx;ClientSecret=xxx",
"TokenOptions": {
"Issuer": "https://identity.company.com",
"Audience": "customer-portal-api",
"SymmetricSigningKey": "base64-encoded-key"
}
}
Environment Variables
| Variable | Description |
|---|---|
FLOWON_PROJECT_NAME | Flowon Project name |
FLOWON_API_NAME | API Builder name |
FLOWON_API_DISPLAY_NAME | Display name for Swagger |
FLOWON_URL_PATH | Base URL path for the API |
Dynamics365ConnectionString | Dataverse connection string |
Authentication Configuration
API Builder Runtime supports JWT Bearer authentication:
builder.Services.AddAuthentication(cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience
};
});
Swagger / OpenAPI Integration
In development, the runtime automatically generates OpenAPI documentation:
if (builder.Environment.IsDevelopment())
{
builder.Services.AddEndpointsApiExplorer().AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo()
{
Version = "v1",
Title = "Customer Portal API (Development)"
});
// Add Bearer token authentication to Swagger UI
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
});
}
Access Swagger UI at: https://localhost:port/swagger
Observability
API Builder Runtime supports OpenTelemetry for distributed tracing and metrics:
builder.Services
.AddOpenTelemetry()
.ConfigureResource(resource =>
{
resource
.AddService(serviceName)
.AddAttributes(new Dictionary<string, object>
{
["deployment.environment"] = builder.Environment.EnvironmentName,
["k8s.namespace.name"] = Environment.GetEnvironmentVariable("NAMESPACE"),
["k8s.pod.name"] = Environment.GetEnvironmentVariable("HOSTNAME")
});
})
.WithTracing(builder =>
builder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter())
.WithMetrics(builder =>
builder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter());
Deployment Considerations
Container Deployment
API Builder Runtime is typically deployed as a container:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyCompany.Api/MyCompany.Api.csproj", "MyCompany.Api/"]
RUN dotnet restore "MyCompany.Api/MyCompany.Api.csproj"
COPY . .
WORKDIR "/src/MyCompany.Api"
RUN dotnet build -c Release -o /app/build
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyCompany.Api.dll"]
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: customer-portal-api
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: myregistry/customer-portal-api:latest
env:
- name: FLOWON_PROJECT_NAME
value: "CustomerPortal"
- name: FLOWON_API_NAME
value: "CustomerPortalAPI"
- name: Dynamics365ConnectionString
valueFrom:
secretKeyRef:
name: api-secrets
key: dynamics365-connection-string
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
Health Checks
Add health check endpoints for Kubernetes liveness and readiness probes:
builder.Services.AddHealthChecks()
.AddCheck("dynamics365", new DataverseHealthCheck(connectionString));
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false // Liveness: just check if app is running
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});