Demarbit.Shared.Api 1.0.4

dotnet add package Demarbit.Shared.Api --version 1.0.4
                    
NuGet\Install-Package Demarbit.Shared.Api -Version 1.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Demarbit.Shared.Api" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Demarbit.Shared.Api" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Demarbit.Shared.Api" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Demarbit.Shared.Api --version 1.0.4
                    
#r "nuget: Demarbit.Shared.Api, 1.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Demarbit.Shared.Api@1.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Demarbit.Shared.Api&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Demarbit.Shared.Api&version=1.0.4
                    
Install as a Cake Tool

Demarbit.Shared.Api

Shared ASP.NET Core API library for DDD / Clean Architecture projects. Provides a configurable ProblemDetails exception filter, correlation ID middleware, security headers middleware, session resolution middleware, OpenAPI transformers for Optional<T>, and DI/pipeline wiring.

Logging provider and auth strategy agnostic — consumers configure their own Serilog/NLog, JWT Bearer/OAuth/API keys, CORS, rate limiting, and OpenAPI setup.

Installation

dotnet add package Demarbit.Shared.Api

The consumer adds their own project-specific packages:

# Authentication (pick one)
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

# Logging (recommended, not required)
dotnet add package Serilog.AspNetCore

# OpenAPI (if used)
dotnet add package Microsoft.AspNetCore.OpenApi

Dependencies

Package Purpose
Demarbit.Shared.Domain DomainException for exception mapping
Demarbit.Shared.Application AppException hierarchy, OptionalJsonConverterFactory, ISessionContext
Microsoft.AspNetCore.App Framework reference: ProblemDetails, IExceptionFilter, middleware, health checks, JSON
Microsoft.AspNetCore.OpenApi IOpenApiDocumentTransformer for Optional<T> and content type transformers

Quick Start

1. Register Services

using Demarbit.Shared.Api.Extensions;

// Basic setup with defaults:
services.AddSharedApi();

// Or with customizations:
services.AddSharedApi(options =>
{
    // Disable HSTS for local development
    options.SecurityHeaders.StrictTransportSecurity = null;

    // Add project-specific exception mappings
    options.ExceptionMapping.Map<PaymentDeclinedException>(
        402, "Payment Required", "https://httpstatuses.io/402");
});

This registers:

  • ProblemDetailsExceptionFilter (global exception → ProblemDetails)
  • ICorrelationIdAccessor (access correlation ID from any service)
  • OptionalJsonConverterFactory (PATCH semantics JSON support)
  • Liveness health check ("self")

2. Configure the Pipeline

using Demarbit.Shared.Api.Extensions;
using Demarbit.Shared.Api.Middleware;

var app = builder.Build();

// Shared middleware (security headers → correlation ID → error handler):
app.UseSharedApi();

// Project-specific middleware:
app.UseSerilogRequestLogging();      // consumer adds Serilog
app.UseCors();                       // consumer configures CORS
app.UseRateLimiter();                // consumer configures rate limiting
app.UseAuthentication();             // consumer configures auth
app.UseAuthorization();
app.UseMiddleware<SessionResolutionMiddleware>();

// Endpoints:
app.MapControllers();
app.MapSharedHealthChecks();         // /healthz (live) + /readyz (ready)

await app.RunAsync();

3. Implement Session Resolution

using Demarbit.Shared.Api.Contracts;

// Your project's implementation:
internal sealed class AppSessionResolver(IUserProfileRepository userRepo) : ISessionResolver
{
    public async Task ResolveAsync(
        HttpContext context,
        ISessionContext session,
        ClaimsPrincipal user)
    {
        var authIdClaim = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (Guid.TryParse(authIdClaim, out var authId))
        {
            var profile = await userRepo.FindByAuthIdAsync(authId);
            if (profile is not null)
            {
                session.SetUserId(profile.Id);
                session.SetLocale(profile.Locale);
                session.SetTimeZone(profile.TimeZone);
            }
        }
    }
}

// Register in DI:
services.AddScoped<ISessionResolver, AppSessionResolver>();

If no ISessionResolver is registered, the middleware is a no-op.

4. Standard Program.cs

using Serilog;
using Demarbit.Shared.Api.Extensions;
using Demarbit.Shared.Api.Middleware;
using MyApp.Application.DependencyInjections;
using MyApp.Infrastructure.DependencyInjections;

var builder = WebApplication.CreateBuilder(args);

// Logging (consumer's choice — Serilog recommended)
builder.Host.UseSerilog((ctx, services, cfg) =>
{
    cfg.ReadFrom.Configuration(ctx.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext();
});

// Request size limits
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
});

// Layer registration
builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddSharedApi();

// Auth (consumer's choice)
builder.Services.AddAuthentication(/* ... */).AddJwtBearer(/* ... */);
builder.Services.AddAuthorization();

// CORS (consumer's choice)
builder.Services.AddCors(/* ... */);

// Session resolver
builder.Services.AddScoped<ISessionResolver, AppSessionResolver>();

var app = builder.Build();

// Pipeline
app.UseSharedApi();
app.UseSerilogRequestLogging();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<SessionResolutionMiddleware>();

if (app.Environment.IsDevelopment())
    app.MapOpenApi();

app.MapControllers();
app.MapSharedHealthChecks();

if (!app.Environment.IsEnvironment("Testing"))
    await app.Services.BootstrapDatabaseAsync<AppDbContext>();

await app.RunAsync();

namespace MyApp.Api
{
    public partial class Program { }
}

The shared library provides JwtOptions for configuration binding but does not register auth — that's project-specific:

using Demarbit.Shared.Api.Configuration;

var jwtOptions = builder.Configuration.GetSection(JwtOptions.SectionName).Get<JwtOptions>()
    ?? throw new ConfigurationException(JwtOptions.SectionName);

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(bearer =>
{
    bearer.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateLifetime = true,
        ValidAudience = jwtOptions.Audience,
        ValidIssuer = jwtOptions.Issuer,
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(jwtOptions.Secret)),
        ClockSkew = TimeSpan.Zero
    };
});

builder.Services.AddAuthorization();

6. OpenAPI with Shared Transformers

using Demarbit.Shared.Api.OpenApi.Transformers;

services.AddOpenApi("v1", opts =>
{
    // Shared transformers from the library:
    opts.AddDocumentTransformer<OptionalSchemaDocumentTransformer>();
    opts.AddDocumentTransformer<RemoveUnnecessaryContentTypesTransformer>();

    // Project-specific customizations:
    opts.AddDocumentTransformer((document, context, ct) =>
    {
        document.Info.Title = "My API";
        document.Info.Description = "My API description.";
        return Task.CompletedTask;
    });
});

7. Health Checks

using Demarbit.Shared.Api.Extensions;
using Demarbit.Shared.Infrastructure.Extensions;

// Readiness checks (consumer adds their own):
builder.Services.AddHealthChecks()
    .AddDbContextHealthCheck<AppDbContext>(tags: ["ready"])
    .AddNpgSql(connectionString, name: "postgres", tags: ["ready"]);

// Endpoints (in pipeline):
app.MapSharedHealthChecks();
// Maps:
//   /healthz → liveness  (checks tagged "live" — self-check always healthy)
//   /readyz  → readiness (checks tagged "ready" — database, etc.)

Exception → ProblemDetails Mapping

Default mappings (most specific first):

Exception Status Title
ValidationFailedException 400 Validation failed
NotFoundException 404 Not found
ForbiddenException 403 Forbidden
ConflictException 409 Conflict
AppException (catch-all) 400 Application error
DomainException 422 Domain rule violation
Unmatched Exception 500 Unexpected error

All responses include traceId and correlationId extensions. ValidationFailedException responses include an errors extension with structured validation errors. Development mode includes stack traces and inner exception details.

What's Included

Type Purpose
ProblemDetailsExceptionFilter Configurable exception → ProblemDetails with trace/correlation IDs
CorrelationIdMiddleware Reads/generates X-Correlation-ID, stores for downstream access
SecurityHeadersMiddleware Configurable security headers with sensible defaults
SessionResolutionMiddleware Delegates to consumer's ISessionResolver
ISessionResolver Consumer implements user identity resolution
ICorrelationIdAccessor Access correlation ID from any scoped service
SharedApiOptions Top-level options (exception mapping + security headers)
SecurityHeadersOptions Individual header values, nullable to disable
ExceptionProblemDetailsMap Configurable exception type → status/title/URI mapping
ProblemDetailsDescriptor Resolved mapping result
JwtOptions Universal JWT configuration (audience, issuer, secret)
HttpHeaders Constants for header names (X-Correlation-ID, etc.)
OpenApiGroupTitleAttribute Group title metadata for controllers
OptionalSchemaDocumentTransformer Fixes Optional<T> OpenAPI schemas
RemoveUnnecessaryContentTypesTransformer Strips non-JSON content types
ServiceCollectionExtensions.AddSharedApi() DI registration
WebApplicationExtensions.UseSharedApi() Middleware pipeline
WebApplicationExtensions.MapSharedHealthChecks() /healthz + /readyz endpoints

Package Family

Demarbit.Shared.Domain               ← zero deps
    ↑
Demarbit.Shared.Application          ← Domain + M.E.DI + M.E.Logging
    ↑                    ↑
Demarbit.Shared.Infrastructure       Demarbit.Shared.Api
  ↑ (EF Core)                          ↑ (ASP.NET Core)
  |                                     |
  +--------- [Consumer Project] --------+
              adds provider NuGets, auth, Serilog, CORS, etc.

Note: Demarbit.Shared.Api does not depend on Demarbit.Shared.Infrastructure. This preserves the Clean Architecture dependency rule — the API layer knows nothing about EF Core or database concerns.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.4 92 2/20/2026