Shop It Docs
Developer Resourcesbanners

Banners Feature Overview

Banners Module — Feature Overview

1. Purpose

Centralized database-driven banner management. Covers: marketing banners, ad placements, sponsored content, scheduled campaigns, contextual targeting, impression/click analytics, and a cacheable public serving API. All banner state is owned by the backend — no static configuration required.


2. Route Surface Ownership

SurfaceBase PathAuth
Admin — Banners/admin/bannersJWT + Banners_* permission
Admin — Placements/admin/placementsJWT + Placements_* permission
Admin — Campaigns/admin/campaignsJWT + Campaigns_* permission
Admin — Campaign Placements/admin/campaigns/:publicId/placementsJWT + Assignments_* permission
Admin — Banner Assignments/admin/campaigns/:publicId/placements/:placementPublicId/bannersJWT + Assignments_* permission
Admin — Targeting Rules/admin/targeting-rulesJWT + TargetingRules_* permission
Public — Serving/banners/serve/:slugNone (@Public() class decorator)
Public — Tracking/banners/events/impression, /banners/events/clickNone (@Public() class decorator)

3. Admin Capability Matrix

3.1 Banner Management

ActionEndpointNotes
Create bannerPOST /admin/bannersAdmin-only. createdBy set from JWT admin id.
List bannersGET /admin/bannersPaginated, filterable by isActive, scheduleStatus, brandId, categoryId, includeDeleted, search.
Get bannerGET /admin/banners/:publicId
Update bannerPATCH /admin/banners/:publicIdPartial update; invalidates list cache on success.
Soft-delete bannerDELETE /admin/banners/:publicIdSets deletedAt. Throws BANNER_ALREADY_DELETED if already deleted.
Restore bannerPOST /admin/banners/:publicId/restoreClears deletedAt. Throws BANNER_NOT_DELETED if not deleted.
Update schedulePATCH /admin/banners/:publicId/scheduleUpdates scheduleStatus, publishAt, expiresAt, recurrence window.
ActivatePATCH /admin/banners/:publicId/activateSets isActive=true.
DeactivatePATCH /admin/banners/:publicId/deactivateSets isActive=false.

3.2 Placement Management

ActionEndpointNotes
Create placementPOST /admin/placementsSlug globally unique; layoutType enum required.
List placementsGET /admin/placementsPaginated, filterable.
Get placementGET /admin/placements/:publicId
Update placementPATCH /admin/placements/:publicId
Soft-delete placementDELETE /admin/placements/:publicId
ActivatePATCH /admin/placements/:publicId/activate
DeactivatePATCH /admin/placements/:publicId/deactivate

3.3 Campaign Management

ActionEndpointNotes
Create campaignPOST /admin/campaignsStatus is always set to draft by the service.
List campaignsGET /admin/campaignsPaginated, filterable by status, source, includeDeleted, search.
Get campaignGET /admin/campaigns/:publicId
Update campaignPATCH /admin/campaigns/:publicId
Soft-delete campaignDELETE /admin/campaigns/:publicIdThrows CAMPAIGN_ALREADY_DELETED if already deleted.
Restore campaignPOST /admin/campaigns/:publicId/restoreThrows CAMPAIGN_NOT_DELETED if not deleted.
Lifecycle: draft→scheduledPATCH /admin/campaigns/:publicId/scheduleRequires startsAt in the future.
Lifecycle: scheduled/active/paused→activePATCH /admin/campaigns/:publicId/activate
Lifecycle: active/paused→pausedPATCH /admin/campaigns/:publicId/pause
Lifecycle: active/paused→endedPATCH /admin/campaigns/:publicId/end
Lifecycle: any non-terminal→cancelledPATCH /admin/campaigns/:publicId/cancelIrreversible.

3.4 Assignment Management

ActionEndpointNotes
Attach placement to campaignPOST /admin/campaigns/:publicId/placementsBody: { placementPublicId, isActive? }. Creates campaign_placements row.
Detach placement from campaignDELETE /admin/campaigns/:publicId/placements/:placementPublicId
Activate/deactivate placement link`PATCH /admin/campaigns/:publicId/placements/:placementPublicId/activatedeactivate`
List assignmentsGET /admin/campaigns/:publicId/placements/:placementPublicId/banners
Assign banner to slotPOST /admin/campaigns/:publicId/placements/:placementPublicId/bannersBody: AssignBannerDto. Composite-PK row.
Update banner assignmentPATCH /admin/campaigns/:publicId/placements/:placementPublicId/banners/:bannerPublicId
Remove banner assignmentDELETE /admin/campaigns/:publicId/placements/:placementPublicId/banners/:bannerPublicId
Activate/deactivate banner assignment`PATCH /admin/campaigns/:publicId/placements/:placementPublicId/banners/:bannerPublicId/activatedeactivate`

3.5 Targeting Rule Management

ActionEndpointNotes
Add targeting rulePOST /admin/targeting-rulesBody: { campaignPublicId, ruleType, operator, value, isActive? }.
List targeting rulesGET /admin/targeting-rules?campaignPublicId=...Filtered by campaign.
Update targeting rulePATCH /admin/targeting-rules/:publicId
Delete targeting ruleDELETE /admin/targeting-rules/:publicId

4. Public Serving Capability

FeatureDetail
EndpointGET /banners/serve/:slug
Rate limit300 req/min per IP (configurable via @IpThrottle)
CacheRedis, TTL from BANNER_SERVE_CACHE_TTL_SECONDS (default 60s)
Cache keybanners:serve:<slug>:<ctx_hash> (single prefix; per-context segmentation)
AuthNone — @Public() class decorator bypasses JwtAuthGuard
TargetingEvaluates all active rules for the campaign against the incoming request context. AND logic.
Schedule checkFilters banners by scheduleStatus + publishAt/expiresAt + recurrence window (HH:MM:SS string compare)
Fallback bannersUsed only when primary slots unfilled AND placement.allowPartialRender=true
Response shape{ placement, banners[], servedAt }servedAt is always new Date().toISOString() at serve time, NEVER cached

5. Public Event Tracking Capability

FeatureDetail
Impression endpointPOST /banners/events/impression
Click endpointPOST /banners/events/click
Rate limit600 req/min per IP (@IpThrottle)
AuthNone — @Public() class decorator
Response202 Accepted (fire-and-forget)
IdempotencycorrelationId used as BullMQ jobId — duplicate events silently no-op
Event storageAsync via BullMQ BANNERS queue to banner_impressions / banner_clicks tables

6. Business Rules

6.1 Banner Lifecycle

scheduleStatus is managed by admin actions and by the SYNC_SCHEDULE_STATUS cron job. Banner serving eligibility:

scheduleStatusEligible to serve?
expiredNever
evergreenYes (subject to recurrence window)
scheduledOnly if publishAt <= now AND (expiresAt IS NULL OR expiresAt > now)
activepublishAt <= now AND (expiresAt IS NULL OR expiresAt > now)

expired is never served regardless of other conditions. The cron transitions scheduled→active and active→expired based on time.

6.2 Campaign Lifecycle

VALID_TRANSITIONS map (module-level constant in CampaignsAdminService):

FromAllowed → To
draftscheduled, active
scheduledactive, cancelled
activepaused, ended, cancelled
pausedactive, ended, cancelled
ended(none — terminal)
cancelled(none — terminal)

Transitions are enforced via CAMPAIGN_INVALID_TRANSITION error. schedule requires startsAt > now AND current status in ["draft", "active", "paused"].

6.3 Targeting Evaluation Rules

  • All targeting rules for a campaign are evaluated with AND logic.
  • A campaign with zero targeting rules passes all requests (serve to everyone).
  • 9 rule types: device, country, user_segment, login_state, language, hour_of_day, day_of_week, new_visitor, referrer_domain.
  • user_segment always returns true (deferred implementation).
  • Targeting exceptions (e.g., rule evaluation throwing) are caught and logged as warn — the rule is treated as passing (fail-open).

6.4 Slot Fill and Fallback

  • placement.maxBanners is the hard cap on banners per request.
  • bannerAssignments.isFallback distinguishes primary from fallback; fallbackPriority orders fallbacks.
  • Primary banners (non-fallback) fill first, ordered by displayOrder ASC, weight DESC.
  • If primary slots are unfilled AND placement.allowPartialRender=true, fallback banners fill remaining slots in fallbackPriority ASC order.
  • If allowPartialRender=false and primary slots are unfilled, the response has an empty banners array.

6.5 SEO Integration

  • Creating/updating a banner with seoId links the banner to an SEO record. Throws BANNER_SEO_NOT_FOUND if the SEO id does not exist.
  • The SEO module's getUsage() and remove() should consider active banner references.

6.6 Money Representation

  • Campaign budgets are stored as bigint columns with _paisa suffix (budgetTotalPaisa, budgetDailyPaisa).
  • No float or numeric types for monetary values.
  • API DTOs accept and return amounts in paisa (integer).

7. Permissions Reference

PermissionCode format: Module_ACTION (e.g., Banners_READ). All admin endpoints use @UseGuards(JwtAuthGuard, RoleGuard) + @Permissions(...).

PermissionUsed For
Banners_READList/get banners (admin)
Banners_CREATECreate banner
Banners_UPDATEUpdate, schedule, activate, deactivate, restore
Banners_DELETESoft-delete banner
Placements_READ / Placements_CREATE / Placements_UPDATE / Placements_DELETEPlacement admin endpoints
Campaigns_READ / Campaigns_CREATE / Campaigns_UPDATE / Campaigns_DELETECampaign admin endpoints (incl. lifecycle transitions)
Assignments_READ / Assignments_CREATE / Assignments_UPDATE / Assignments_DELETECampaign placements + banner assignments
TargetingRules_READ / TargetingRules_CREATE / TargetingRules_UPDATE / TargetingRules_DELETETargeting rules

8. Integration Dependencies

SystemDirectionPurpose
RedisRead/WriteServing endpoint cache (key pattern invalidation via banners:serve:*)
BullMQ (Redis)Producer + ConsumerAsync impression/click writes, schedule sync (minutely), stats rollup (nightly)
SEO moduleRead (banner create/update)seoId FK reference
Admin UsersFK referencecreatedBy on all admin-created entities
CustomersFK referenceuserId on impression/click events (nullable)