Configuration

OtterApi is configured entirely in Program.cs through two calls: AddOtterApi to register services, and UseOtterApi to register the middleware.

AddOtterApi

Two overloads are available:

Program.cs
// Overload 1 — path only
services.AddOtterApi<AppDbContext>("/api");

// Overload 2 — full configuration
services.AddOtterApi<AppDbContext>(options =>
{
    options.Path        = "/api";
    options.MaxPageSize = 100;
    options.Entity<Product>("products");
    options.Entity<Category>("categories").Authorize();
});

OtterApiOptions properties

PropertyTypeDefaultDescription
Path string required Base URL prefix for all generated routes. Example: /api/v1
MaxPageSize int 1000 Server-side cap on items per page. Any client-supplied ?pagesize= exceeding this value is silently clamped. Set to 0 to disable the limit (use with caution on large tables). Applies to regular list requests, /pagedresult, and custom routes.
JsonSerializerOptions JsonSerializerOptions? null Global serialization/deserialization options. See below.

UseOtterApi

Program.cs
app.UseAuthentication();
app.UseAuthorization();

app.UseOtterApi();   // ← must be here

app.MapControllers();

UseOtterApi() registers the middleware that intercepts incoming HTTP requests, matches them against registered entities, and executes CRUD operations.

⚠️
UseOtterApi() must be placed after UseAuthentication() and UseAuthorization(), and before UseEndpoints() / MapControllers(). Incorrect order will cause authorization to be bypassed or routes to not be found.

JsonSerializerOptions

Global serialization options apply to both incoming request bodies (POST, PUT) and outgoing responses.

Program.cs
services.AddOtterApi<AppDbContext>(options =>
{
    options.Path = "/api";
    options.JsonSerializerOptions = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented        = false
    };
    options.Entity<Product>("products");
});
💡
Always added automatically: PropertyNameCaseInsensitive = true and the OtterApiCaseInsensitiveEnumConverterFactory are always applied regardless of your custom settings.
⚠️
PATCH special case: PATCH bodies are deserialized with a fresh default JsonSerializerOptions (JsonSerializerDefaults.Web) at the document level, regardless of your custom options. Custom naming policies and converters are applied when individual field values are deserialized from the patch node. All other verbs (POST, PUT) fully respect your custom options.