Skip to main content

Attribute Sets

Attribute Sets group ProductAttribute definitions into reusable schemas. Every Product is wired to exactly one Attribute Set; the system ships a default set that applies when admins do not pick one.

Why they exist​

Without sets, every Product carries its full attribute graph in attributeValues. Different product types (electronics, apparel, chemicals) need different attributes, but the foundation schema treated every key as global. Sets let admins curate a focused authoring experience per category and let the storefront render a tighter attribute table.

Public surface​

Verb + PathAudiencePurpose
GET /api/v1/admin/catalog/attribute-setsadminList all sets including default
GET /api/v1/admin/catalog/attribute-sets/:idadminSet detail with assigned attributes
POST /api/v1/admin/catalog/attribute-setsadminCreate custom set (code snake_case, unique)
PATCH /api/v1/admin/catalog/attribute-sets/:idadminUpdate name; system Default is immutable
DELETE /api/v1/admin/catalog/attribute-sets/:idadminDelete; rejects with 409 when any Product references the set
POST /api/v1/admin/catalog/attribute-sets/:id/attributesadminAssign attributes to the set
DELETE /api/v1/admin/catalog/attribute-sets/:id/attributes/:keyadminUnassign

The Product create/update payload accepts attributeSetId. When omitted, the system Default Set is used.

Errors​

CodeStatusWhen
ATTRIBUTE_SET_CODE_TAKEN409code already exists
ATTRIBUTE_SET_IN_USE409Delete blocked: at least one Product references the set
SYSTEM_ATTRIBUTE_SET_IMMUTABLE409Mutating the seeded Default Set
ATTRIBUTE_SET_NOT_FOUND404:id missing
ATTRIBUTE_NOT_FOUND404:key missing on assignment

Storefront integration​

productDetail.attributeSet carries {id, code, name} so themes can render the set label above the attribute table. Foundation reference theme renders the localized set name as a small subheading.

Storage​

attribute_sets (id, code unique, name jsonb, is_system bool) + attribute_set_attributes (composite PK on (set_id, attribute_id)). The system Default row is seeded by migration 017 with deterministic UUID defa0017-0000-4000-8000-000000000000 so seeds and tests can reference it stably.

Audit log​

AttributeSet CRUD writes one AuditLogEntry per mutation with stateBefore and stateAfter so the audit page surfaces who changed what.