Catalogue & product data
Attribute Management
Generated from docs/plan/core/attribute-management/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Core feature for defining product attributes (Brand, Manufacturer, Material, Size, Color) that can be reused across many products. Merchants create an attribute library, assign attributes and values to products, and filter/search products by attribute. This enables consistent product metadata and faceted navigation.
The problem
Merchants can’t define Brand, Manufacturer, or Material consistently; product data is inconsistent.
What it does
- Create attribute: name (e.g., ‘Brand’, ‘Color’, ‘Material’), type (string, enum, integer, color-hex), optional description
- Attribute values (enums): for enum-type attributes, define the allowed values (e.g., Color: [Red, Blue, Green])
- Assign to products: when editing a product, add attribute fields (e.g., Brand=‘Nike’, Material=‘Cotton’)
- Attribute-value picker: when assigning, show autocomplete or dropdown with predefined values (for enums)
- Reuse values: when assigning ‘Brand=Nike’ to a product, if Nike already exists, reuse it; don’t create duplicate
- Global attribute list: GET /attributes returns all defined attributes with value counts
- Product attributes: each product can have multiple attributes (max 30 per product), with user-visible toggle (show in storefront or admin-only)
- Filter by attribute: GET /products?brand=nike returns products with that attribute value
- Attribute hierarchy (optional): some attributes have hierarchies (e.g., Color > Hue > Saturation), but typically flat
- Bulk attribute assignment: assign attribute values to many products via bulk edit
- Attribute cleanup: merge duplicate attribute values (e.g., ‘nike’, ‘Nike’, ‘NIKE’ → ‘Nike’)
- Attribute import/export: import attribute list with values from CSV
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Variant attributes — that’s variant-management; variant options (size, color) are separate from product attributes
- Attribute-based pricing — use categories or promotions; attributes are metadata only
- Attribute-specific validation rules (e.g., ‘Brand must not be empty’) — stored as optional; no required attributes in core
- Localized attribute names/values — attributes are in the shop’s primary language only
- Complex attribute relationships (if Brand=Nike, then Material must be textile) — no conditional logic
Data model
New entities: attribute (id, name: string (unique, max 100), type: ‘string’ | ‘enum’ | ‘integer’ | ‘color’, description: text (nullable), created_at), attribute_value (id, attribute_id, value: string (or color hex), is_visible: boolean, created_at). Link through product_attributes join table (product_id, attribute_id, value_id or value_string). Product.attributes field stores normalized attribute name/value pairs.
API
- GET /attributes — list all attributes with type and value counts
- POST /attributes — create attribute {name, type, description?}
- PUT /attributes/:id — update attribute name/description
- DELETE /attributes/:id — delete attribute, remove from all products
- GET /attributes/:id/values — list all values for an attribute with usage count
- POST /attributes/:id/values — add a value to an enum attribute {value}
- POST /attributes/:id/merge-values — merge duplicate values {source_value_id, target_value_id}
- GET /products?attribute_:name=:value — filter products by attribute (e.g., /products?attribute_brand=nike)
- POST /products/:id/attributes — assign attribute to product {attribute_name, value}
- DELETE /products/:id/attributes/:attribute_id — remove attribute from product
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Attribute management panel: (1) Attribute list (all attributes, type, value count, create/edit/delete buttons), (2) Create/edit modal (name, type selector, description WYSIWYG), (3) Enum values management (list values for enum-type attributes, add/remove values, reorder), (4) Merge values UI (select two values, merge to one), (5) Product edit form: attribute section (show attribute fields, text input or dropdown based on type, autocomplete for enums), (6) Bulk assign attributes (select products, assign attribute values to all).
The seam — why this is core
Core owns attribute schema, CRUD, and product linking. Storefront could use attributes for filtering/facets, but that’s storefront responsibility. A paid tier could own attribute-driven merchandising (e.g., featured brands, attribute-specific layouts).
Core owns the interface + honest attribute editor; product metadata is infrastructure, not a per-country obligation or credential.
Dependencies
- product-management (core; products have attributes)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- POST /attributes with {name: ‘Brand’, type: ‘enum’} creates attribute and returns {id: ‘attr_1’, name: ‘Brand’, type: ‘enum’}.
- POST /attributes/attr_1/values with {value: ‘Nike’} adds value to Brand attribute.
- Assigning ‘Brand=Nike’ to product; if Nike already exists as a value, reuse it (no duplicate Brand=nike created).
- GET /attributes/attr_1/values returns [{value: ‘Nike’, usage_count: 45}, {value: ‘Adidas’, usage_count: 30}].
- GET /products?attribute_brand=nike returns all products with Brand=Nike.
- Deleting attribute ‘Brand’ (DELETE /attributes/attr_1) removes the attribute from all products.
- Merging values (Nike, nike) to ‘Nike’ updates all products with nike to use Nike.
- Assigning attribute with is_visible=true shows it in storefront product detail; is_visible=false hides it (admin only).
Risks
If attribute names or values are not deduplicated, the same brand appears as ‘Nike’, ‘nike’, ‘NIKE’. If deletion is not cascaded, products still reference deleted attributes. If enum values are changed after products are assigned, old values become orphaned.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: included (Custom fields); Magento: included |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.