Skip to content

Getting Started

This guide walks you through building your first Arc.Core application from scratch. You’ll learn how to set up the application, define commands and queries, and run your service.

  • .NET 9 SDK or later
  • Basic understanding of C# and .NET concepts

Add the Arc.Core package to your project:

Terminal window
dotnet add package Cratis.Arc

Create a new console application and configure Arc.Core:

using Cratis.Arc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = ArcApplication.CreateBuilder(args);
// Add Arc services
builder.AddCratisArc(options => options.Hosting.ApplicationUrl = "http://localhost:5000/");
// Configure logging
builder.Services.AddLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Information);
});
// Build and run the application
var app = builder.Build();
// Wire up the Arc middleware and endpoints
app.UseCratisArc();
Console.WriteLine("Application started on http://localhost:5000/");
Console.WriteLine("Press Ctrl+C to stop...");
await app.RunAsync();

The ArcApplicationBuilder provides a familiar builder pattern for configuring your application:

// Create with command-line arguments
var builder = ArcApplication.CreateBuilder(args);
// Or without arguments
var builder = ArcApplication.CreateBuilder();

The builder exposes several properties for configuration:

// Configuration system
IConfigurationManager Configuration = builder.Configuration;
// Host environment information
IHostEnvironment Environment = builder.Environment;
// Logging configuration
ILoggingBuilder Logging = builder.Logging;
// Service collection for dependency injection
IServiceCollection Services = builder.Services;
// Metrics configuration
IMetricsBuilder Metrics = builder.Metrics;
builder.AddCratisArc(
configureOptions: options =>
{
// Configure Arc-specific options (ArcOptions)
},
configureBuilder: arcBuilder =>
{
// Add extensions like Chronicle, MongoDB, etc.
});

The UseCratisArc method wires up the Arc middleware and endpoints. It takes no arguments:

// UseCratisArc takes no arguments — it wires up the middleware and endpoints.
app.UseCratisArc();

The listen URL is not passed here — it comes from configuration via ArcOptions.Hosting.ApplicationUrl (default http://+:5001/). Set it through the options callback when adding Arc services, or in appsettings.json under Cratis:Arc:Hosting:ApplicationUrl.

// Run and block until shutdown (Ctrl+C)
await app.RunAsync();
// Or control start/stop manually
await app.StartAsync();
// ... do work ...
await app.StopAsync();

Commands represent actions or operations in your application. They’re automatically exposed as HTTP POST endpoints.

using Cratis.Arc.Commands;
[Command]
public record CreateUser(string Name, string Email)
{
public Task Handle(ILogger<CreateUser> logger)
{
logger.LogInformation("Creating user: {Name}", Name);
// Your business logic here
return Task.CompletedTask;
}
}

Commands are exposed as POST endpoints:

Terminal window
curl -X POST http://localhost:5000/api/your-app/create-user \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com"}'

Queries represent data retrieval operations. They’re automatically exposed as HTTP GET endpoints.

using Cratis.Arc.Queries;
[ReadModel]
public record User(Guid Id, string Name)
{
// Exposed as GET; the method's parameters become query arguments.
public static User GetUser(Guid id) => new(id, "John Doe");
}

Queries are exposed as GET endpoints:

Terminal window
curl http://localhost:5000/api/your-app/get-user?id=123e4567-e89b-12d3-a456-426614174000

Arc.Core supports standard .NET configuration:

{
"Cratis": {
"Arc": {
"GeneratedApis": {
"RoutePrefix": "api"
},
"CorrelationId": {
"HttpHeader": "X-Correlation-ID"
},
"Tenancy": {
"HttpHeader": "X-Tenant-ID"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}

Use environment-specific files following .NET conventions:

  • appsettings.json - Base configuration
  • appsettings.Development.json - Development overrides
  • appsettings.Production.json - Production overrides
var builder = ArcApplication.CreateBuilder(args);
// Configuration is automatically loaded based on environment
var environment = builder.Environment.EnvironmentName;
Console.WriteLine($"Running in {environment} environment");

Specify a custom configuration section path:

builder.AddCratisArc(
configSectionPath: "MyApp:ArcSettings"
);
builder.Services.AddLogging(logging =>
{
logging.AddConsole();
logging.AddDebug();
logging.SetMinimumLevel(LogLevel.Information);
});
// Singleton
builder.Services.AddSingleton<IMyService, MyService>();
// Scoped (per request)
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Transient (per injection)
builder.Services.AddTransient<IEmailSender, EmailSender>();

Arc supports automatic service registration using attributes:

// Services with [Singleton], [Scoped], or [Transient] attributes
// are automatically registered
[Singleton]
public class MyService : IMyService
{
// Implementation
}

Here’s a complete working example that demonstrates commands, queries, and services:

using Cratis.Arc;
using Cratis.Arc.Commands;
using Cratis.Arc.Queries;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = ArcApplication.CreateBuilder(args);
builder.AddCratisArc(options => options.Hosting.ApplicationUrl = "http://localhost:5000/");
builder.Services.AddLogging(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Information);
});
var app = builder.Build();
app.UseCratisArc();
Console.WriteLine("Application started!");
Console.WriteLine("Available endpoints:");
Console.WriteLine(" POST http://localhost:5000/api/my-app/greet");
Console.WriteLine(" GET http://localhost:5000/api/my-app/get-greeting?name=World");
Console.WriteLine(" GET http://localhost:5000/.cratis/me");
Console.WriteLine();
Console.WriteLine("Press Ctrl+C to stop...");
await app.RunAsync();
// Commands
[Command]
public record Greet(string Name)
{
public Task Handle(ILogger<Greet> logger)
{
logger.LogInformation("Greeting {Name}", Name);
return Task.CompletedTask;
}
}
// Queries
[ReadModel]
public record Greeting(string Text)
{
public static Greeting GetGreeting(string name) => new($"Hello, {name}!");
}

Combine Arc.Core with IHostedService for background processing:

public class BackgroundWorker(ILogger<BackgroundWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("Background worker started");
while (!stoppingToken.IsCancellationRequested)
{
// Do background work
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
logger.LogInformation("Background worker stopped");
}
}
// Register it
builder.Services.AddHostedService<BackgroundWorker>();

Add event sourcing capabilities:

builder.AddCratisArc(configureBuilder: arcBuilder =>
{
arcBuilder.WithChronicle();
});

Add MongoDB support:

builder.AddCratisArc(configureBuilder: arcBuilder =>
{
arcBuilder.WithMongoDB();
});

Ensure you’ve called app.UseCratisArc() before app.RunAsync():

var app = builder.Build();
app.UseCratisArc(); // Must be called!
await app.RunAsync();

If you get HTTP listener errors, ensure:

  1. The port is not already in use
  2. You have permissions to bind to the port (on Windows, non-admin users can’t bind to port 80)
  3. Set the listen URL via ArcOptions.Hosting.ApplicationUrl (in the options callback or appsettings.json under Cratis:Arc:Hosting:ApplicationUrl) — it is not passed to UseCratisArc. Use http://+:5001/ instead of http://localhost:5001/ to listen on all interfaces

Ensure appsettings.json is copied to output:

<ItemGroup>
<None Update="appsettings*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

Now that you have a basic Arc.Core application running, explore these topics:

  • Authentication - Implement custom authentication handlers
  • Authorization - Protect your endpoints with authorization attributes
  • Commands - Learn about advanced command patterns
  • Queries - Discover query features like filtering and pagination
  • Identity - Integrate the identity system
  • Tenancy - Configure multi-tenant applications
  • Validation - Add validation to commands and queries