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
| Property | Type | Default | Description |
|---|---|---|---|
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.