Seeding the catalogue
One generated file, one idempotent script, and the division by 100 that decides whether perfume costs RM108 or RM10,800.
The 13 SKUs are defined once, in the storefront, and flow to Medusa through a generated file. Two steps, and the second one is safe to re-run.
1. Regenerate the seed data
npm run seed:catalogscripts/gen-catalog.mjs reads apps/storefront/src/lib/products.ts and writes
apps/medusa/src/scripts/glemor-catalog.ts:
// GENERATED — do not edit by hand.
// Source: apps/storefront/src/lib/products.ts
export const CATALOG = { products: [ /* 13 SKUs */ ] }A .ts module rather than .json, so medusa build is guaranteed to ship it. Edit the
storefront catalogue and regenerate; never edit the generated file.
2. Seed Medusa
cd apps/medusa
npx medusa exec ./src/scripts/seed-glemor.tsEvery step is find-or-create, so the script is idempotent — re-running it is safe, and is how you pick up new products.
It creates, if missing:
- the Glemor Online sales channel, and points the store's default at it;
- the store itself, named Glemor Fragrance, with MYR as the default currency;
- a Malaysia / MYR region covering
my; - three collections — Premium Series, Daily Series, Discovery Sets;
- a stock location, a shipping profile and the tax region;
- 13 products, published, in the sales channel;
- a publishable API key, linked to the sales channel.
It finishes by printing the publishable key, the sales channel id and the region id. Put
the key in the storefront as NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY.
The two things that go wrong
Money. The storefront stores sen; Medusa v2 stores decimal amounts. The seed writes
priceSen / 100. Invert that and every bottle is priced at a hundred times its value —
and because /api/checkout re-reads prices from the same catalogue, that is what a
customer is charged.
prices: [{ amount: p.priceSen / 100, currency_code: 'myr' }]The region. getCatalog() resolves the MYR region by currency code and refuses to
guess. Medusa's own demo seed leaves a Europe/EUR region behind that sorts first; products
carry no EUR price, calculated_price comes back null, and the mapper would produce
0 sen. It is a fallback with a logged error instead.
Products that already exist
Existing handles are skipped, not updated. To change a price or a description on a product that is already there, edit it in the admin — that is the source of truth once a product exists. The seed is for standing a new environment up, not for pushing edits.