* Regenerate delivery api claud memory file for updated file lines and inclusion of Secure Cookie-Based Token Storage * Add delivery api memory file * claude memory file for in memory modelsbuilder project * Claud memory file for Imagesharp project * Claude memory file for legacy image sharp project * Claude memory files for Persistence projects * Remaining claude memory files
12 KiB
Umbraco.Cms.Api.Delivery
Headless content delivery REST API for Umbraco CMS. Enables frontend applications to fetch published content, media, and member-protected resources.
1. Architecture
Type: Class Library (NuGet Package) Target Framework: .NET 10.0 Purpose: Content Delivery API for headless CMS scenarios
Key Technologies
- ASP.NET Core - Web framework
- OpenIddict - Member authentication (OAuth 2.0)
- Asp.Versioning - API versioning (V1, V2)
- Output Caching - Configurable response caching
- Examine/Lucene - Content querying
Dependencies
Umbraco.Cms.Api.Common- Shared API infrastructure (OpenAPI, auth)Umbraco.Web.Common- Web functionality
Project Structure (86 files)
Umbraco.Cms.Api.Delivery/
├── Controllers/
│ ├── Content/ # Content endpoints (by ID, route, query)
│ ├── Media/ # Media endpoints (by ID, path, query)
│ └── Security/ # Member auth (authorize, token, signout)
├── Querying/
│ ├── Filters/ # ContentType, Name, CreateDate, UpdateDate
│ ├── Selectors/ # Ancestors, Children, Descendants
│ └── Sorts/ # Name, CreateDate, UpdateDate, Level, SortOrder
├── Indexing/ # Lucene index field handlers
├── Services/ # Business logic and query building
├── Caching/ # Output cache policies
├── Rendering/ # Output expansion strategies
├── Configuration/ # Swagger configuration
└── Filters/ # Action filters (access, validation)
Design Patterns
- Strategy Pattern - Query handlers (
ISelectorHandler,IFilterHandler,ISortHandler) - Factory Pattern -
ApiContentQueryFactorybuilds Examine queries - Template Method -
ContentApiControllerBasefor shared controller logic - Options Pattern -
DeliveryApiSettingsfor all configuration
2. Commands
See "Quick Reference" section at bottom for common commands.
3. Key Patterns
API Versioning (V1 vs V2)
V1 (legacy) and V2 (current) coexist. Key difference is output expansion:
// DependencyInjection/UmbracoBuilderExtensions.cs:49-52
// V1 uses RequestContextOutputExpansionStrategy
// V2+ uses RequestContextOutputExpansionStrategyV2
return apiVersion.MajorVersion == 1
? provider.GetRequiredService<RequestContextOutputExpansionStrategy>()
: provider.GetRequiredService<RequestContextOutputExpansionStrategyV2>();
Why V2: Improved expand and fields query parameter parsing (tree-based).
Query System Architecture
Content querying flows through handlers registered in DI:
- Selectors (
fetchparameter):ancestors:id,children:id,descendants:id - Filters (
filter[]parameter):contentType:alias,name:value,createDate>2024-01-01 - Sorts (
sort[]parameter):name:asc,createDate:desc,level:asc
// Services/ApiContentQueryService.cs:91-96
ISelectorHandler? selectorHandler = _selectorHandlers.FirstOrDefault(h => h.CanHandle(fetch));
return selectorHandler?.BuildSelectorOption(fetch);
Path Decoding Workaround
ASP.NET Core doesn't decode forward slashes in route parameters:
// Controllers/DeliveryApiControllerBase.cs:21-31
// OpenAPI clients URL-encode paths, but ASP.NET Core doesn't decode "/"
// See https://github.com/dotnet/aspnetcore/issues/11544
if (path.Contains("%2F", StringComparison.OrdinalIgnoreCase))
{
path = WebUtility.UrlDecode(path);
}
4. Testing
Location: No direct tests - tested via integration tests in test projects
dotnet test tests/Umbraco.Tests.Integration/ --filter "FullyQualifiedName~Delivery"
Internals exposed to (csproj lines 28-36):
Umbraco.Tests.UnitTestsUmbraco.Tests.IntegrationDynamicProxyGenAssembly2(for mocking)
Focus areas:
- Query parsing (selectors, filters, sorts)
- Member authentication flows
- Output caching behavior
- Protected content access
5. Security & Access Control
Three Access Modes
// Services/ApiAccessService.cs:21-27
public bool HasPublicAccess() => _deliveryApiSettings.PublicAccess || HasValidApiKey();
public bool HasPreviewAccess() => HasValidApiKey();
public bool HasMediaAccess() => _deliveryApiSettings is { PublicAccess: true, Media.PublicAccess: true } || HasValidApiKey();
Access levels:
- Public - No authentication required (if enabled)
- API Key - Via
Api-Keyheader - Preview - Always requires API key
Member Authentication
OpenIddict-based OAuth 2.0 for member-protected content:
Flows supported (Controllers/Security/MemberController.cs):
- Authorization Code + PKCE (line 53)
- Client Credentials (line 112)
- Refresh Token (line 98)
Endpoints:
GET /umbraco/delivery/api/v1/security/member/authorizePOST /umbraco/delivery/api/v1/security/member/tokenGET /umbraco/delivery/api/v1/security/member/signout
Scopes: Only openid and offline_access allowed for members (line 220-222)
Protected Content
Member access checked via ProtectedAccess model:
// Controllers/Content/QueryContentApiController.cs:56-57
ProtectedAccess protectedAccess = await _requestMemberAccessService.MemberAccessAsync();
Attempt<PagedModel<Guid>, ApiContentQueryOperationStatus> queryAttempt =
_apiContentQueryService.ExecuteQuery(fetch, filter, sort, protectedAccess, skip, take);
6. Output Caching
Cache Policy Configuration
// DependencyInjection/UmbracoBuilderExtensions.cs:120-136
// Content and Media have separate cache durations
options.AddPolicy(
Constants.DeliveryApi.OutputCache.ContentCachePolicy,
new DeliveryApiOutputCachePolicy(
outputCacheSettings.ContentDuration,
new StringValues([AcceptLanguage, AcceptSegment, StartItem])));
Cache invalidation conditions (Caching/DeliveryApiOutputCachePolicy.cs:31):
// Never cache preview or non-public access
context.EnableOutputCaching = requestPreviewService.IsPreview() is false
&& apiAccessService.HasPublicAccess();
Vary by headers: Accept-Language, Accept-Segment, Start-Item
7. Edge Cases & Known Issues
Technical Debt (TODOs in codebase)
-
V1 Removal Pending (4 locations):
DependencyInjection/UmbracoBuilderExtensions.cs:98- FIXME: remove matcher policyRouting/DeliveryApiItemsEndpointsMatcherPolicy.cs:11- FIXME: remove classFilters/SwaggerDocumentationFilterBase.cs:79,83- FIXME: remove V1 swagger docs
-
Obsolete Reference Warnings (csproj:9-13):
ASP0019- IHeaderDictionary.Append usageCS0618/CS0612- Obsolete member references
Empty Query Results
Query service returns empty results (not errors) for invalid options:
// Services/ApiContentQueryService.cs:54-78
// Invalid selector/filter/sort returns fail status with empty result
return Attempt.FailWithStatus(ApiContentQueryOperationStatus.SelectorOptionNotFound, emptyResult);
Start Item Fallback
When no fetch parameter provided, uses start item or all content:
// Services/ApiContentQueryService.cs:99-112
if (_requestStartItemProviderAccessor.TryGetValue(out IRequestStartItemProvider? requestStartItemProvider))
{
IPublishedContent? startItem = requestStartItemProvider.GetStartItem();
// Use descendants of start item
}
return _apiContentQueryProvider.AllContentSelectorOption(); // Fallback to all
8. Project-Specific Notes
V1 vs V2 Differences
| Feature | V1 | V2 |
|---|---|---|
| Output expansion | Basic | Tree-based parsing |
expand parameter |
Flat list | Nested syntax |
fields parameter |
Limited | Full property selection |
| Default expansion strategy | RequestContextOutputExpansionStrategy |
RequestContextOutputExpansionStrategyV2 |
Migration note: V1 is deprecated; plan removal when V17+ drops V1 support.
JSON Configuration
Delivery API has its own JSON options (distinct from Management API):
// DependencyInjection/UmbracoBuilderExtensions.cs:82-88
.AddJsonOptions(Constants.JsonOptionsNames.DeliveryApi, options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.TypeInfoResolver = new DeliveryApiJsonTypeResolver();
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
Member Token Revocation
Tokens automatically revoked on member changes:
// DependencyInjection/UmbracoBuilderExtensions.cs:93-96
builder.AddNotificationAsyncHandler<MemberSavedNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<MemberDeletedNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<AssignedMemberRolesNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
builder.AddNotificationAsyncHandler<RemovedMemberRolesNotification, RevokeMemberAuthenticationTokensNotificationHandler>();
External Dependencies
Examine/Lucene (via Core):
- Powers content querying
- Selector/Filter/Sort handlers build Lucene queries
OpenIddict (via Api.Common):
- Member OAuth 2.0 authentication
- Reference tokens (not JWT)
Configuration (appsettings.json)
{
"Umbraco": {
"CMS": {
"DeliveryApi": {
"Enabled": true,
"PublicAccess": true,
"ApiKey": "your-api-key",
"Media": {
"Enabled": true,
"PublicAccess": true
},
"MemberAuthorization": {
"AuthorizationCodeFlow": { "Enabled": true },
"ClientCredentialsFlow": { "Enabled": false }
},
"OutputCache": {
"Enabled": true,
"ContentDuration": "00:01:00",
"MediaDuration": "00:01:00"
}
}
}
}
}
API Endpoints Summary
Content (/umbraco/delivery/api/v2/content):
GET /item/{id}- Single content by GUIDGET /item/{path}- Single content by routeGET /items- Multiple by IDsGET /- Query with fetch/filter/sort
Media (/umbraco/delivery/api/v2/media):
GET /item/{id}- Single media by GUIDGET /item/{path}- Single media by pathGET /items- Multiple by IDsGET /- Query media
Security (/umbraco/delivery/api/v1/security/member):
GET /authorize- Start OAuth flowPOST /token- Exchange code for tokenGET /signout- Revoke session
Quick Reference
Essential Commands
# Build project
dotnet build src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj
# Pack for NuGet
dotnet pack src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj -c Release
# Run integration tests
dotnet test tests/Umbraco.Tests.Integration/ --filter "FullyQualifiedName~Delivery"
# Check packages
dotnet list src/Umbraco.Cms.Api.Delivery/Umbraco.Cms.Api.Delivery.csproj package --outdated
Key Classes
| Class | Purpose | File |
|---|---|---|
DeliveryApiControllerBase |
Base controller with path decoding | Controllers/DeliveryApiControllerBase.cs |
ApiContentQueryService |
Query orchestration | Services/ApiContentQueryService.cs |
ApiAccessService |
Access control logic | Services/ApiAccessService.cs |
DeliveryApiOutputCachePolicy |
Cache policy implementation | Caching/DeliveryApiOutputCachePolicy.cs |
MemberController |
OAuth endpoints | Controllers/Security/MemberController.cs |
RequestContextOutputExpansionStrategyV2 |
V2 output expansion | Rendering/RequestContextOutputExpansionStrategyV2.cs |
Important Files
Umbraco.Cms.Api.Delivery.csproj- Project dependenciesDependencyInjection/UmbracoBuilderExtensions.cs- DI registration (lines 33-141)Configuration/DeliveryApiConfiguration.cs- API constantsServices/ApiContentQueryService.cs- Query execution
Getting Help
- Root documentation:
/CLAUDE.md- Repository overview - API Common patterns:
/src/Umbraco.Cms.Api.Common/CLAUDE.md - Official docs: https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api
- Media docs: https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api/media-delivery-api
This library exposes Umbraco content and media via REST for headless scenarios. Focus on query handlers, access control, and member authentication when working here.