Skip to content

refactor(api-headless-cms-ddb,api-headless-cms-ddb-es,api-headless-cms-sql): fold CMS storage back into one root feature - #5970

Merged
adrians5j merged 2 commits into
nextfrom
refactor/cms-storage-container-scope
Oct 9, 2026
Merged

adrians5j merged 2 commits into
nextfrom
refactor/cms-storage-container-scope

Conversation

@adrians5j

@adrians5j adrians5j commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Why

#5933 split each CMS storage package in two: a feature registered in the root container (tables, entities) and a *RequestFeature registered per request (storage operations, DataLoaders, filter registries, OpenSearch extension points). That was the only way to get one instance per request. The catch was that every event handler had to know which feature to register where, through registerRequestStorage.

@webiny/di 2.0 (webiny/di#19, adopted in #5969) adds inContainerScope(): registered once in the root, but built and cached in each container that resolves it, which means once per request. So the split is no longer needed.

What changes

Each CMS storage package is one feature again, registered once in the root. Each package now declares its own per-request state:

Now inContainerScope() Where
Entry DataLoaders api-headless-cms-ddb, api-headless-cms-ddb-es
CMS filter registries (path, value transform, filter create, sorting) api-headless-cms-storage
OpenSearch field indexes, field index registry, model index provider api-headless-cms-utils-os
DbRegistry @webiny/db

The filter registries were built with new and registered as instances. Each now has a small Default*Registry subclass that registers its defaults in the constructor, so the registries can be container scoped. They're mutable (extensions call register()), so one shared instance would collect every request's registrations.

DbRegistry is public (webiny/api/db) and has always been per request, so it stays per request through inContainerScope(). The DDB+OpenSearch storage used to add its two entities from inside the request feature. Now it does that through a decorator registered once in the root (CmsEntitiesDbRegistryDecorator). That works because 2.0 applies decorators to every container-scoped instance.

Removed:

What stays the same

  • Extension decorators still apply to CMS storage operations. 2.0 collects decorators from the resolving container for transient and container-scoped registrations. cmsStorageDecorators.test.ts covers this through the real DDB handler.
  • One DataLoader cache per request. cmsDataLoadersPerRequest.test.ts covers this through the real handler.

Tests

  • The four DDB handler tests (cmsDataLoadersPerRequest, cmsStorageDecorators, freshInstall, featureFlagsBuildParam) pass with no request features registered anywhere.
  • dataLoadersScope.test.ts is rewritten for the new setup. Storage is registered only in the root. It checks one DataLoaders instance per request and per container, and that each request gets its own filter registries.
  • New api-headless-cms-ddb-es/__tests__/perRequestState.test.ts: each request gets its own DbRegistry with the CMS entities, items registered in one request don't show up in the next, and the field index registry is built per request.
  • The full build, adio, format, lint and sync-dependencies are clean.
  • Locally: api-headless-cms-ddb 43, api-headless-cms-sql 4, api-headless-cms-storage 15, api-core 267. The full CMS suites (DDB and SQL) and ACO were still running when I opened this. We'll check those in CI with /vitest.

Next

Step 2 from the design doc: check ACO, audit logs and websockets storage. They're root factories. Under 2.0, a factory's decorators come from the resolving container, so extension decorators may already reach them. I'll test that before changing anything.

Workspace: wby-next11 · STORAGE PER REQ

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements
    • CMS storage handling is more consistent across DynamoDB, DynamoDB with OpenSearch, and SQL.
    • Request-specific caches, registries, and search indexes remain isolated, while repeated access within a request reuses its own state.
  • Bug Fixes
    • Improved separation of database entity registrations and other request data to prevent state from carrying between requests.
  • Tests
    • Added coverage for request-level state isolation and cache reuse within each request.

…s-sql): fold CMS storage back into one root feature

#5933 split each CMS storage package into a root feature and a request
feature, because @webiny/di had no per-request lifetime: the only way to get
one instance per request was to register in the child container. The event
handlers then had to know which feature goes where, through
registerRequestStorage.

@webiny/di 2.0 adds inContainerScope(): registered once in the root, built and
cached per resolving (request) container. So each storage package is one
feature again, registered once in the root, and declares per-request state
itself:

- entry DataLoaders, the CMS filter registries, and the OpenSearch field
  indexes, field index registry and model index provider are container scoped;
- DbRegistry is container scoped, and the DDB+OS storage adds its entities to
  each request's registry through a decorator registered in the root;
- the *RequestFeature features (unreleased) and the registerRequestStorage hook
  are removed.

Extension decorators keep applying, because 2.0 collects decorators from the
resolving container for transient and container-scoped registrations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adrians5j

Copy link
Copy Markdown
Member Author

/vitest

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Vitest tests have been initiated (for more information, click here). ✨

Group Status
No storage ✅ 74/74 passed
DDB ✅ 17/17 passed
DDB+OS ✅ 19/19 passed
SQL ✅ 12/12 passed
PGlite ✅ 12/12 passed

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🚓 Slop Cop

✅ Nothing worth flagging. The diff looks consistent with the PR's stated intent and the code-style rules.

Coherent refactor matching its stated intent, with no leaked secrets, debug code, or style violations in the added/changed lines.

Automated, non-blocking heads-up from an LLM. It can be wrong — use your judgment. Regenerates on every push.

@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9b9664f7-b0e9-45a4-9b6e-1bf7ee3abea7

📥 Commits

Reviewing files that changed from the base of the PR and between 32af5ae and cad7846.


📒 Files selected for processing (1)
  • packages/api-headless-cms-ddb-es/src/registry/CmsEntitiesDbRegistryDecorator.ts

🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/api-headless-cms-ddb-es/src/registry/CmsEntitiesDbRegistryDecorator.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.



📝 Walkthrough

Walkthrough

CMS storage operations and registries now register through the main storage features instead of separate request features. Handler configuration no longer provides request-phase storage registration. Container-scoped registries and per-request tests reflect the updated registration model.

Changes

CMS storage registration and request state

Layer / File(s) Summary
Container-scoped registries and state
packages/api-headless-cms-storage/src/features/*, packages/api-headless-cms-ddb/src/operations/entry/feature.ts, packages/api-headless-cms-ddb-es/src/operations/entry/feature.ts, packages/api-headless-cms-utils-os/src/features/*, packages/db/src/features/DbRegistry/feature.ts
Default filter and sorting registry implementations are added and registered by class. DbRegistry, OpenSearch index services, and DataLoaders handlers use container scope.
CMS feature registration consolidation
packages/api-headless-cms-ddb/src/*, packages/api-headless-cms-ddb-es/src/*, packages/api-headless-cms-sql/src/*
DynamoDB, DynamoDB with OpenSearch, and SQL features register storage operations and related features. Their separate request features and public exports are removed. The DDB-ES feature adds a registry decorator that registers CMS entities.
Handler root-storage registration
packages/api-event-handler-aws*/src/*, packages/api-event-handler-core/src/registerApiRequestStack.ts, packages/api-event-handler-standalone*/src/*, packages/db-dynamodb/src/testing.ts
Handler configuration and request-stack registration no longer expose or pass a request-storage callback. DynamoDB and SQL handlers use their main feature registrations.
Request-scope tests and setup
packages/api-headless-cms-ddb-es/__tests__/*, packages/api-headless-cms-ddb/__tests__/*, packages/api-headless-cms-sql/__tests__/*
Test setup removes separate request-feature registration. Tests check registry and DataLoader isolation between child containers and repeated resolution within one request.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Refactor


Merge Risk: ⚪ Minimal · up to cad78

The CMS entity registration change has no identified merge-blocking issue; merge after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 23 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title accurately summarizes the main change: consolidating DDB, DDB+OpenSearch, and SQL CMS storage into root-registered features. It is specific and concise.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@adrians5j

Copy link
Copy Markdown
Member Author

/e2e

@adrians5j

Copy link
Copy Markdown
Member Author

/vitest

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Cypress E2E tests have been initiated (for more information, click here). ✨

Database Status Admin URL
DDB ❌ Failed -
DDB+OS ❌ Failed -
Standalone (SQLite) ✅ Passed -
Standalone (Postgres) ✅ Passed -
Standalone (SQLite, WCP) ❌ Failed -
Standalone (Postgres, WCP) ❌ Failed -

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Vitest tests have been initiated (for more information, click here). ✨

Group Status
No storage ✅ 74/74 passed
DDB ✅ 17/17 passed
DDB+OS ✅ 19/19 passed
SQL ✅ 12/12 passed
PGlite ✅ 12/12 passed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adrians5j
adrians5j merged commit 36d85ff into next Oct 9, 2026
23 checks passed
@adrians5j
adrians5j deleted the refactor/cms-storage-container-scope branch October 9, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant