Banner Module — Storefront
HTTP surface for storefront banner reads. The customer-facing app fetches active, platform-targeted promotional banners attached to a catalog entity (category / brand / tag /…
HTTP surface for storefront banner reads. The customer-facing app fetches active, platform-targeted promotional banners attached to a catalog entity (category / brand / tag / ingredient) and renders them in carousels and entity-page heroes.
Source:
api-modules/banner/src/controllers/public-banner.controller.ts.Admin CRUD for banners lives in
docs/separated/admin/banner.md. This file documents the public read endpoint only.
Conventions
Authentication
| Endpoint | Auth |
|---|---|
GET /store/banners/:entityType/slug/:slug | none (public) |
Banners are public marketing content — no session is required and no PII is returned.
Response envelope
Successful responses are wrapped by ResponseInterceptor:
{
"data": <payload>,
"message": "Success",
"statusCode": 200,
"metadata": { /* optional */ }
}The response is not paginated — banners attached to a single entity are bounded by the admin who configured them.
Error envelope
statusCode | errorCode examples |
|---|---|
| 400 | BAD_REQUEST (unknown entityType) |
| 500 | INTERNAL_SERVER_ERROR, DATABASE_ERROR |
A missing slug does not 404 — the response is an empty array. The endpoint deliberately can't distinguish "no banners attached" from "no such entity" so consumers render the same empty state in both cases.
Domain types
BannerResponse
type BannerEntityType = "category" | "brand" | "tag" | "ingredient";
type Platform = "APP" | "WEB" | "BOTH";
type BannerResponse = {
id: string;
entityType: BannerEntityType;
entityId: string;
image: string; // S3 key — resolve to URL client-side via the storage CDN
url: string; // click destination — https:// absolute or /relative
platform: Platform;
isActive: boolean; // always true for storefront reads
sortOrder: number; // ascending; ties broken by createdAt desc
metadata: Record<string, unknown> | null;
createdAt: string; // ISO
updatedAt: string; // ISO
deletedAt: string | null; // always null for storefront reads
};url is constrained server-side to https://... absolutes or /-prefixed relative paths — http://, javascript:, mailto: and other schemes are rejected at admin create-time, so the storefront can use the value verbatim without re-sanitising.
Endpoints
GET /store/banners/:entityType/slug/:slug — Active banners for an entity
Returns active (isActive=true, deletedAt IS NULL) banners attached to the resolved entity, sorted by sortOrder ASC, createdAt DESC.
Path params
| Name | Notes |
|---|---|
entityType | One of category / brand / tag / ingredient. Anything else → 400 BAD_REQUEST with a list of valid values |
slug | Slug of the catalog entity |
Query
| Name | Type | Notes |
|---|---|---|
platform | Platform? | When APP or WEB, hides banners that target the other platform. Absent or BOTH returns every active banner regardless of platform targeting |
Response 200 — array of BannerResponse.
{
"data": [
{
"id": "01J9...",
"entityType": "brand",
"entityId": "01J9...",
"image": "banners/2026-05/festive.jpg",
"url": "/sale",
"platform": "BOTH",
"isActive": true,
"sortOrder": 0,
"metadata": null,
"createdAt": "2026-05-01T08:00:00.000Z",
"updatedAt": "2026-05-01T08:00:00.000Z",
"deletedAt": null
}
],
"message": "Success",
"statusCode": 200
}Errors
| Status | Code | When |
|---|---|---|
| 400 | BAD_REQUEST | entityType is not one of the four allowed values |
Related modules
catalog— owns thecategory/brand/tag/ingredientslugs that this endpoint resolves against. Seecatalog.md.storage— resolvesimagestorage keys to public CDN URLs on the client.
Affiliate Module — Storefront
HTTP surface for the customer-facing affiliate plugin. Customers apply to join, generate trackable referral links, see their commission balance + payout history, and (anonymous…
Cart Module — Storefront
HTTP surface for the storefront shopping cart — guest and logged-in reads/mutates, address attach, coupon apply/remove + browse, free-gift picker, guest→customer merge on login,…