Developer Resourcescontent
Content Module Backend Documentation
Newsletter schema, entitlement model, runtime behavior, and release checklist for content series/items/featured flows.
Content Module - Backend Documentation
1. Scope
Content module manages newsletter publishing, featuring, and subscription-tier entitlement checks.
content_type is intentionally constrained to newsletter in the current contract. The enum stays explicit to preserve extension points for future content families without changing column shape.
2. Newsletter Tables
Primary tables:
content_seriescontent_itemnewsletter_content_tiercontent_series_slug_historycontent_item_slug_historyfeatured_contentcontent_series_tag
Tier-mapping table (newsletter_content_tier):
content_item_idFK tocontent_item.id(ON DELETE CASCADE)series_idFK tocontent_series.id(ON DELETE CASCADE)required_tierenum (basic|premium)- exactly one target set per row (
content_item_idxorseries_id)
3. Entitlement Model
Newsletter full-content access depends on:
- tier mapping via
newsletter_content_tierat item or series level - active
user_feature_access_windowrows for newsletter feature keys - feature keys:
newsletter.basic,newsletter.premium - validity check:
started_at <= nowandended_at IS NULL
Tier resolution behavior:
- no tier mapping means preview/free-access content
basicrequirement is satisfied bybasicorpremiumpremiumrequirement is satisfied only bypremium
4. Runtime Behavior
API surfaces are split by responsibility:
- admin leaf modules: series, items, featured
- customer leaf modules: series, items, featured
- mobile prefix is applied by
MobileModulecomposition
Runtime mode:
- public preview/list endpoints for published content
- JWT-protected full-content and library endpoints
- cache-aside reads with deterministic keys
- prefix invalidation on admin writes
5. Schema Hardening
featured_contenthasfeatured_content_date_window_check- allows
NULLstart/end - blocks invalid windows where
start_date > end_date
- allows
content_series_taghascontent_series_tag_tag_id_idx- optimizes tag-to-series lookups for tag-filtered browse flows
newsletter_content_tier_exactly_one_target- enforces one target (
content_item_idxorseries_id)
- enforces one target (
- full-text + trigram indexes support title/body and slug search paths in customer lookups
6. Integration Dependencies
- subscription module (
subscription_module,user_feature_access_window) for entitlement - notification flows for newsletter email and push delivery
- Redis for cache and rate-limiting counters
7. Runtime Configuration
Rate limits:
CONTENT_ADMIN_RATE_LIMIT(default100)CONTENT_ADMIN_RATE_WINDOW_SECONDS(default60)CONTENT_CUSTOMER_ITEM_RATE_LIMIT(default30)CONTENT_CUSTOMER_ITEM_RATE_WINDOW_SECONDS(default60)CONTENT_CUSTOMER_SERIES_RATE_LIMIT(default100)CONTENT_CUSTOMER_SERIES_SEARCH_RATE_LIMIT(default50)CONTENT_CUSTOMER_SERIES_RATE_WINDOW_SECONDS(default60)
Cache TTL:
CONTENT_CUSTOMER_SERIES_CACHE_TTL_SECONDS(default1800)CONTENT_CUSTOMER_ITEM_CACHE_TTL_SECONDS(default900)CONTENT_CUSTOMER_MY_LIBRARY_CACHE_TTL_SECONDS(default60)CONTENT_CUSTOMER_PURCHASES_CACHE_TTL_SECONDS(default60)CONTENT_CACHE_TTL_SECONDS(optional override for featured runtime)REDIS_CACHE_TTL_SECONDS(global fallback)
8. Error Handling
| HTTP | errorCode | Message |
|---|---|---|
| 400 | CONTENT_STATUS_INVALID | Cannot set status=published when scheduledAt is in the future. |
| 400 | CONTENT_ARCHIVED_NOT_PUBLISHABLE | Archived content cannot be published. |
| 400 | CONTENT_SCHEDULED_AT_INVALID | Invalid scheduledAt value. |
| 400 | CONTENT_SERIES_CREATE_FAILED | Failed to create content series. |
| 400 | CONTENT_NOT_PUBLISHED | Cannot send unpublished newsletter. |
| 403 | CONTENT_ACCESS_DENIED | You do not have access to this content. |
| 404 | CONTENT_NOT_FOUND | Content item not found. |
| 404 | CONTENT_SERIES_NOT_FOUND | Content series not found. |
| 404 | CONTENT_CATEGORY_NOT_FOUND | Category with ID <id> not found. |
| 404 | TAG_NOT_FOUND | Tag not found for id(s): <ids>. |
| 404 | FEATURED_CONTENT_NOT_FOUND | Featured content not found. |
| 400 | FEATURED_CONTENT_INVALID_DATE_RANGE | Featured startDate must be before or equal to endDate. |
| 503 | ERR_CONFIG_INVALID | Configuration validation failed. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. |
9. Backend Diagram
10. Module Composition
ContentModule- shared module- imports:
ConfigModule,DatabaseModule,RedisModule,TrainingModule
- imports:
ContentAdminAggregateModule- admin aggregateContentAdminModule: series CRUDContentItemAdminModule: item CRUDContentFeaturedAdminModule: featured management
ContentCustomerAggregateModule- customer aggregateContentCustomerModule: series + items + libraryContentFeaturedCustomerModule: featured listing
11. File Map
| Concern | File Path | Purpose |
|---|---|---|
| Admin series | apps/api/src/modules/content/admin/series/* | Series CRUD |
| Admin content | apps/api/src/modules/content/admin/content/* | Content item CRUD |
| Admin featured | apps/api/src/modules/content/admin/content-featured/* | Featured management |
| Customer series | apps/api/src/modules/content/customer/series/* | Series discovery |
| Customer content | apps/api/src/modules/content/customer/content/* | Content item access |
| Customer featured | apps/api/src/modules/content/customer/content-featured/* | Featured listing |
| DB schema | packages/db/src/schema/content/* | Table definitions |
12. Release/QA Checklist
- Series, item, and featured admin/customer routes resolve from leaf modules.
- Entitlement checks enforce tier gating on full-content endpoints.
- Newsletter tier mapping enforces exactly-one-target invariant.
- Featured date-window check rejects invalid start/end ranges.
- Cache key prefixes are deterministic and invalidated on content mutations.
- Rate-limit envs are present and pass startup validation.
- Structured errors return stable
errorCodevalues for all major failure paths.
13. Environment Variables
| Variable | Default | Description |
|---|---|---|
CONTENT_ADMIN_RATE_LIMIT | 100 | Rate limit for admin content operations |
CONTENT_ADMIN_RATE_WINDOW_SECONDS | 60 | Rate limit window for admin operations |
CONTENT_CUSTOMER_ITEM_RATE_LIMIT | 30 | Rate limit for customer content item requests |
CONTENT_CUSTOMER_ITEM_RATE_WINDOW_SECONDS | 60 | Rate limit window for customer item requests |
CONTENT_CUSTOMER_SERIES_RATE_LIMIT | 100 | Rate limit for customer series requests |
CONTENT_CUSTOMER_SERIES_SEARCH_RATE_LIMIT | 50 | Rate limit for series search requests |
CONTENT_CUSTOMER_SERIES_RATE_WINDOW_SECONDS | 60 | Rate limit window for customer series |
CONTENT_CUSTOMER_SERIES_CACHE_TTL_SECONDS | 1800 | Cache TTL for customer series list |
CONTENT_CUSTOMER_ITEM_CACHE_TTL_SECONDS | 900 | Cache TTL for customer item detail |
CONTENT_CUSTOMER_MY_LIBRARY_CACHE_TTL_SECONDS | 60 | Cache TTL for customer library |
CONTENT_CUSTOMER_PURCHASES_CACHE_TTL_SECONDS | 60 | Cache TTL for customer purchases |
CONTENT_CACHE_TTL_SECONDS | - | Optional override for featured runtime |
REDIS_CACHE_TTL_SECONDS | - | Global fallback cache TTL |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Content - Feature List Section 6 (State Models) for content lifecycle and tier gating diagrams.
- API Reference: See Content - API & Integration Guide Section 11 (Endpoint Reference + Payload Cheatsheet) for API contracts.