FlowOn API Runtime
The FlowOn API Runtime is the execution engine that hosts and serves your configured APIs. It's implemented as an ASP.NET Core application that reads your FlowOn API configuration from Dynamics 365 and exposes it as REST endpoints.
Runtime Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ FLOWON API RUNTIME ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────┐ │
│ │ Client │ │ FlowOn API Runtime │ │
│ │ Application │ │ │ │
│ └──────┬──────┘ │ ┌─────────────────────────────┐ │ │
│ │ │ │ ASP.NET Core Host │ │ │
│ │ HTTP Request │ │ │ │ │
│ ▼ │ │ ┌───────────────────────┐ │ │ │
│ ┌─────────────┐ │ │ │ REST Server │ │ │ │
│ │ Gateway │───────►│ │ │ (FlowOn.Xrm.Api) │ │ │ │
│ │ (Optional) │ │ │ └───────────┬───────────┘ │ │ │
│ └─────────────┘ │ │ │ │ │ │
│ │ │ ▼ │ │ │
│ │ │ ┌───────────────────────┐ │ │ │
│ │ │ │ Metadata Cache │ │ │ │
│ │ │ │ (API Configuration) │ │ │ │
│ │ │ └───────────┬───────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └──────────────┼──────────────┘ │ │
│ │ │ │ │
│ └─────────────────┼───────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Dynamics 365 / Dataverse │ │
│ │ │ │
│ │ ┌───────────┐ ┌─────────────┐ │ │
│ │ │ FlowOn │ │ Business │ │ │
│ │ │ Config │ │ Data │ │ │
│ │ └───────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hosting Project Structure
A FlowOn API 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 FlowOn API 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 Dynamics 365 connection string
var dataverseConnectionString = builder.Configuration["Dynamics365ConnectionString"];
// Configure FlowOn API 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 FlowOn API services:
builder.Services.AddFlowOnApi(
projectName, // FlowOn Project name
apiName, // FlowOn API name
serviceClientFactory // Factory for Dynamics 365 connection
);
| Parameter | Description |
|---|---|
| projectName | The name of the FlowOn Project containing the API |
| apiName | The name of the FlowOn API to host |
| serviceClientFactory | Factory function that creates the Dynamics 365 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 Dynamics 365 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 Dynamics 365 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 | FlowOn API name |
FLOWON_API_DISPLAY_NAME | Display name for Swagger |
FLOWON_URL_PATH | Base URL path for the API |
Dynamics365ConnectionString | Dataverse connection string |
Authentication Configuration
FlowOn API 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
FlowOn API 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
FlowOn API 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")
});