Happy House - Ecommerce Docs
Developer ResourcesWishlist

Wishlist API Reference

Complete API contracts for the Wishlist module, including routes, auth, DTOs, responses, errors, examples, and integration notes.

Wishlist - API Reference

Audience: Frontend engineers, mobile engineers, backend engineers, QA, and API consumers. Scope: The four customer-facing wishlist endpoints. No admin surface exists.

1. Documentation Evidence

AreaFiles InspectedWhat Was Verified
Controllersapps/api/src/modules/wishlist/customer/wishlist-customer.controller.tsRoutes, methods, guards, status codes
DTOscustomer/dto/*.tsQuery enums, validation
Servicescustomer/wishlist-customer.service.tsBehavior, cache, errors
Schemapackages/db/src/schema/wishlist/wishlist-item.tsUnique constraint, cascades, cap
Error registryapps/api/src/common/types/error-codes.ts (// WISHLIST)WISHLIST_* codes

2. Module Summary

FieldValue
Module namewishlist
Module slugwishlist
Primary actorscustomer
API surfacesmobile only
Base route prefixes/api/mobile/wishlist
Auth modelJwtAuthGuard
PersistencePostgreSQL (wishlist_item), Redis (30s membership cache)
Runtime source of truthwishlist_item rows + live product rows
Sibling docsBackend, Features and flows

3. Concepts and Terminology

TermMeaningSource FileUsed By
productIdThe product public_id (uuid v7), never an integercontrollersSave/remove
savedAtwishlist_item.created_atschemaList
status.availableWhether the product is buyable right nowserviceList
status.reasonWhy an unavailable item is unavailableserviceList
summary.totalItemsThe whole wishlist count, not the page/filtered setserviceList
pagination=falseEverything in one response, bounded at 200serviceList

4. API Surface Map

SurfaceMethodPathActorAuth/GuardPermissionControllerPurpose
MobileGET/api/mobile/wishlistCustomerJWT + IpThrottleWishlistCustomerControllerList saved products
MobileGET/api/mobile/wishlist/product-idsCustomerJWT + IpThrottlesameMembership id-set
MobilePUT/api/mobile/wishlist/products/:productIdCustomerJWT + IpThrottlesameSave (200, idempotent)
MobileDELETE/api/mobile/wishlist/products/:productIdCustomerJWT + IpThrottlesameRemove (200, never 404)

5. Auth, Identity, and Permissions

SurfaceGuard/DecoratorIdentity ShapePermissionGuest AllowedNotes
AllJwtAuthGuard, IpThrottlerGuardreq.user.idNoEvery query scoped to the account

Rate limits, keyed on the account: CUSTOMER_READ 60/min (list, product-ids), CUSTOMER_WRITE 20/min (save, remove). No idempotency header is required — idempotency is structural (the unique constraint), not interceptor-based.

6. DTO and Model Reference

6.1 ListWishlistQueryDto

FieldTypeRequiredDefaultValidationNotes
availabilityenumNoavailable | unavailableFilter on buyability
categoryIdstringNoUUID
brandIdstringNoUUID
searchstringNoinherited from QueryDto
sortenumNosavedAtsavedAt | name | price | availabilityavailability is a SQL sort on stored stock status, not a post-query shuffle
orderenumNodescasc | desc
pagination / page / sizeNotrue / 1 / 20inherited from QueryDtopagination=false returns everything, bounded at 200

6.2 Params DTO

WishlistProductParamsDto { productId: string } — UUID v7.

6.3 Response DTOs

WishlistResponseDto:

{
  "customer": { "id": "019fc692-…" },
  "summary": { "totalItems": 42, "lastUpdated": "2026-08-05T10:00:00.000Z" },
  "items": [
    { "id": "019fc692-…",
      "savedAt": "2026-08-05T10:00:00.000Z",
      "status": { "available": true, "reason": null },
      "product": { "basic": {}, "pricing": {}, "classification": { "brand": {} },
                   "media": { "thumbnail": {} }, "status": {}, "inventory": {} } }
  ]
}

WishlistProductIdsDto: { productIds: string[], totalItems: number }. WishlistItemDto: the item shape above.

Notes: product is the storefront card contract — literally the shape GET /products returns, so the component that renders a listing card renders a saved item unchanged, and anything added to the card contract appears here automatically. It is not the detail shape: no gallery, attributes, seo, tags, timestamps or sku.

status.lifecycle survives on the card, which matters here specifically — this is the one storefront surface that legitimately shows a product that can no longer be bought, and status beside it says why.

There is no top-level timestamps block — a wishlist is a set of items, not an entity.

7. Enum Reference

EnumValueMeaningRuntime EffectSource
WISHLIST_SORTSsavedAt / name / price / availabilityList orderingSQL sort with unique tiebreakerdto
WISHLIST_AVAILABILITY_FILTERSavailable / unavailableBuyability filterHides/shows unavailable itemsdto
reason (derived)null / out_of_stock / draft / archived / removedWhy an item is unavailablePrecedence bottom-up: removed > archived > draft > out_of_stock > nullservice

8. Endpoint Reference

8.1 GET /api/mobile/wishlist

Purpose

The wishlist page. Paginated by default; every item resolves live product data. Items whose product was withdrawn, archived or sold out stay in the list with a reason.

Auth and Permissions

JwtAuthGuard, IpThrottlerGuard; CUSTOMER_READ 60/min (account-keyed).

Request

PartRequiredDetails
QueryNoavailability, categoryId, brandId, search, sort, order, pagination, page, size

Response

200 — envelope with data (WishlistResponseDto) + count/currentPage/totalPage when paginated.

{
  "message": "Wishlist fetched successfully",
  "errorCode": null,
  "count": 42, "currentPage": 1, "totalPage": 3,
  "data": {
    "customer": { "id": "019fc692-…" },
    "summary": { "totalItems": 42, "lastUpdated": "2026-08-05T10:00:00.000Z" },
    "items": [
      { "id": "019fc692-…",
        "savedAt": "2026-08-05T10:00:00.000Z",
        "status": { "available": false, "reason": "archived" },
        "product": { "basic": {}, "pricing": {}, "classification": { "brand": {} },
                     "media": { "thumbnail": {} }, "status": {}, "inventory": {} } }
    ]
  }
}

Side Effects

None — read-only; live product resolution via the shared inventory seam.

Error Cases

None beyond auth/rate-limit.

Edge Cases

Empty wishlist → items: [], totalItems: 0, lastUpdated: null. pagination=false → everything, bounded at 200. availability=available hides unavailable items; availability=unavailable lists exactly them.

8.2 GET /api/mobile/wishlist/product-ids

Purpose

One small payload so a listing page can render every heart icon without asking per tile. Use this for hearts on listing/search pages — fetching the full wishlist, or asking per tile, are both the wrong shape.

Response

200{ productIds: [...], totalItems }. Cached server-side for 30 seconds; cleared immediately on the customer's own PUT/DELETE, so their writes are reflected at once, but another device can be up to 30s stale — update local state optimistically from your own writes.

8.3 PUT /api/mobile/wishlist/products/:productId

Purpose

Save a product. Idempotent — saving something already saved succeeds and changes nothing. Only a product currently published or unlisted can be saved; one already saved stays saved after it stops being either.

Auth and Permissions

CUSTOMER_WRITE 20/min (account-keyed).

Request

Empty body. :productId is the product public id (uuid v7).

Response

200never 201 — with the saved item. Identical whether the call created the row or found it, so a retry is safe and never looks like a failure.

Side Effects

wishlist_item insert (ON CONFLICT DO NOTHING); product-ids cache cleared.

Error Cases

HTTPCodeCondition
404WISHLIST_PRODUCT_NOT_FOUNDNo such product, or one the customer may not save (draft, archived, deleted) — deliberately the same code for both, so hidden and nonexistent ids cannot be told apart
409WISHLIST_LIMIT_REACHEDAlready holds 200 saved products — re-saving an item already held succeeds even at the cap

8.4 DELETE /api/mobile/wishlist/products/:productId

Purpose

Remove a product. Always 200 — removing something never saved, or a product id that names no product at all, is the outcome the caller asked for. DELETE never returns 404, deliberately (anti-enumeration).

Response

200 message-only.

Side Effects

Row hard-deleted; product-ids cache cleared.

Error Cases

None — by design. There is deliberately no WISHLIST_ITEM_NOT_FOUND.

9. Flow Diagrams

9.1 Route Ownership

9.2 Request Sequence (save)

9.3 Error Branch (save)

EndpointPagination TypeDefault SizeMax SizeSort FieldsFiltersResult Cap
GET /api/mobile/wishlistoffset page/size, paginated by default20100savedAt (default), name, price, availabilityavailability, categoryId, brandId, search200 total (whole-list bound)

Every sort carries a unique tiebreaker ((sortKey, id)-style) so offset paging over low-cardinality sorts never duplicates or omits items. summary.totalItems is the whole list, not the filtered set.

11. Caching, Jobs, and External Integrations

IntegrationUsed?Details
Redis cacheYesMembership id-set only — 30s (CACHE_TTL.VOLATILE), cleared on the customer's own writes; the list is never cached (must always show live data)
BullMQNo
External APINo

13. Mandatory Deep API Documentation Pack

13.1 Route-by-Route Completeness Matrix

RouteController MethodDTOsService MethodGuardsPermissionsCacheJobsDB TouchesErrorsDocumented?
GET /api/mobile/wishlistlistListWishlistQueryDtoWishlistCustomerService.listJWT+IpThrottlewishlist_item, products, inventoryYes
GET /api/mobile/wishlist/product-idsproductIds…getProductIdsJWT+IpThrottle30swishlist_itemYes
PUT /wishlist/products/:productIdaddWishlistProductParamsDto…addJWT+IpThrottleclearwishlist_item, products404/409Yes
DELETE /wishlist/products/:productIdremoveWishlistProductParamsDto…removeJWT+IpThrottleclearwishlist_itemYes

13.2 Request/Response Exhaustiveness

Covered in §8: empty-body requests (§8.3), success responses (§8.1/8.3), empty-list behavior (§8.1 edge cases), the unavailable-with-reason response (§8.1), domain errors (§8.3 error table), rate-limit behavior (account-keyed 429), the deliberate absence of a 404 on DELETE (§8.4).

13.3 API Diagram Pack

Route ownership (§9.1), sequence per endpoint family (§9.2, backend §7), error decision tree (§9.3), cache flow (backend §8), async/job flow — none (no jobs).

13.4 Consumer Integration Notes

ConsumerRequired KnowledgeFailure HandlingContract Stability
Web frontendproduct-ids for hearts, live product contract reuse, reason display404 on save of withdrawn product → refresh; never 404 on removeStable
Mobile app30s membership cache staleness across devices; optimistic local state from own writes429 → back off; PUT/DELETE retries safe (idempotent)Stable
QASaveable-while-visible / stays-saved asymmetry, reason precedenceReproduce via exact codesStable
Cart (future)Reads stock from the same inventory seam — availability agrees by constructionStable

13.5 API Tradeoffs and Rationale

DecisionChosen BehaviorAlternatives ConsideredWhy This TradeoffRiskMitigation
PUT + unique constraintIdempotent save, 200 both pathsPOST + service checkIdempotency structural, cannot be forgottenConcurrency spec
No soft deleteHard deleteTombstone + partial uniqueRe-add stays one insertNo historyAccepted
DELETE never 404sAlways 200404 on unknownAnti-enumerationIdempotent surpriseDocumented
Live data on readNever snapshottedSnapshot on saveAlways current price/stockRead costBatched seam
30s membership cacheShort TTL300sFill race bounds stalenessCross-device lagCleared on own writes
200-item capBounded readsUnboundedAbuse boundRare overflowApproximate under concurrency

13.6 API Change Impact

ChangeAffected ConsumersBackend ImpactData ImpactMigration Needed?Compatibility Plan
ProductCustomerStatusDto.lifecycle widenedClients with exhaustive switchesDTO unionNoneCompile-time onlyRuntime no-op — discovery surfaces still emit only published

14. Zero-Omission API Checklist

  • Every controller route is documented (§4, §8, §13.1).
  • Every parent route prefix and runtime URL is documented (§2, §4).
  • Every DTO field, enum, default, transform and validator is documented (§6, §7).
  • Every response field and nullable field is documented (§6.3, §8).
  • Every auth, guard, permission and guest identity branch is documented (§5).
  • Every success, validation, not-found, conflict, rate-limit and server-error branch is documented (§8).
  • Every DB read/write, cache hit/miss/invalidation and external call is documented (§11, backend §8).
  • Every route has examples for success responses and representative failures (§8).
  • Every endpoint family has route, sequence and error diagrams (§9, backend §7).
  • Every tradeoff and compatibility risk is documented (§13.5, §13.6).
  • The API doc links to backend and features/flows (§1, See Also).

15. Integration Checklist

  • Every route from controllers is documented.
  • Every DTO field is documented.
  • Every enum value is documented.
  • Every response envelope is documented.
  • Every error code is documented.
  • Every auth guard and permission is documented.
  • Every cache key, queue job and external call is documented.
  • Every diagram matches the current code.
  • The API doc links to backend and features/flows.

See Also