examples/storefront Public
# storefrontThe API behind a small online shop, running on Cloudflare Workers. Products, carts and orders live in D1, the search index in a second D1 database, product images in R2, and payments go through Stripe.There are three Workers:- `storefront-api` (`src/`) is the public API and the only Worker that writes to the database.- `storefront-stripe-webhook` (`workers/stripe-webhook/`) receives Stripe's events and settles orders through storefront-api's `OrdersService` entrypoint.- `storefront-fulfilment` (`workers/fulfilment/`) consumes the `order-events` queue and sends paid orders to the warehouse.The order fulfilment flow on the Architecture tab follows a payment through all three.It is a plain fetch handler with a small router, no framework, and the tests run the real SQL migrations on Node's built-in SQLite, so `npm test` needs nothing but Node 24.## Running it```bashnpm installnpm testnpm run typecheck```To run the API locally with a small seeded catalog:```bashnpm run db:migratenpm run db:seedecho 'ADMIN_TOKEN=dev-admin-token' > .dev.varsnpm run devcurl -X POST localhost:8787/admin/search/rebuild -H 'authorization: Bearer dev-admin-token'```Product images are served from the bucket's custom domain, `IMAGES_URL` in `wrangler.jsonc`. Checkout needs Stripe: add `STRIPE_SECRET_KEY` to `.dev.vars` with a secret key from your Stripe account's test mode.The guide in `.rebasely/kb/guides/local-dev.md` covers the other two Workers and the cron trigger. To add a product and look at the catalog:```bashcurl -X PUT localhost:8787/admin/products/wool-socks \ -H 'authorization: Bearer dev-admin-token' -H 'content-type: application/json' \ -d '{"name":"Wool socks","price_cents":1800,"stock":40}'curl localhost:8787/products```## API| Method and path | What it does || --- | --- || `GET /products` | Active products by name, a page at a time: `?limit=` (1 to 100, default 24) and `?after=` the previous page's `next` || `GET /products/:id` | One active product || `GET /search` | Active products matching `?q=`, best match first, up to `?limit=` || `PUT /admin/products/:id` | Create or replace a product (admin token) || `PUT /admin/products/:id/image` | Upload the product's image (admin token) || `POST /admin/search/rebuild` | Refill the search index from the catalog (admin token) || `POST /carts` | A new, empty cart || `GET /carts/:id` | A cart with its lines and totals || `PUT /carts/:id/items/:product` | Set a quantity, `{ "quantity": n }` || `DELETE /carts/:id/items/:product` | Remove a line || `POST /carts/:id/checkout` | Create the order and its PaymentIntent, `{ email, ship_to }` || `GET /orders/:id` | An order and its status |Errors are JSON, `{ "error": "<code>", "message": "..." }`, with extra fields where they help (an `out_of_stock` error says how many are `available`).## Layout```src/ storefront-api: router, catalog, carts, images, checkout, ordersworkers/ the other Workers, each with its own wrangler.jsoncmigrations/ D1 schema, applied in ordersearch/ the search index's schema (search/migrations/)test/ node --test suites and the D1 and R2 stand-ins.rebasely/ architecture map, knowledge base and CI workflow```The knowledge base in `.rebasely/kb` explains how each part works and why; start at its index.## LicenseMIT