Skip to main content

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
);
ParameterDescription
projectNameThe name of the FlowOn Project containing the API
apiNameThe name of the FlowOn API to host
serviceClientFactoryFactory function that creates the Dynamics 365 service client

AddRestServer

The AddRestServer method configures the REST server:

.AddRestServer(options =>
{
options.VersioningStrategy = new LatestVersionApiVersioningStrategy();
});

Versioning Strategies

StrategyDescriptionUse Case
DraftVersionApiVersioningStrategyUses the Draft versionDevelopment and testing
LatestVersionApiVersioningStrategyUses the latest published versionProduction deployments
SpecificVersionApiVersioningStrategyUses a specific version numberPinned 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

VariableDescription
FLOWON_PROJECT_NAMEFlowOn Project name
FLOWON_API_NAMEFlowOn API name
FLOWON_API_DISPLAY_NAMEDisplay name for Swagger
FLOWON_URL_PATHBase URL path for the API
Dynamics365ConnectionStringDataverse 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")
});