Skip to content
Open-source e-commerce boilerplate

Fork the repo. Ship a store.

A production storefront, admin dashboard, and Stripe checkout in one repo. Swap the backend, keep the app.

0env vars to boot
The bundled demo catalog serves the store until you connect one.
4backends, one interface
Demo, Firebase, Supabase, Postgres. Adapters, not forks.
18demo products
Filters, search, variants, and stock, already populated.
2languages, 4 currencies
Resolved on the server, so the first paint is already right.

One commerce interface. Swap the backend.

Every storefront and admin call goes through a typed commerce module. Change the adapter, keep the app.

.env.local

NEXT_PUBLIC_COMMERCE_BACKEND=firebase
Implemented in
src/lib/commerce/firebase.ts
Credentials
NEXT_PUBLIC_FIREBASE_*

Zero-config default. Firestore rules ship in the repo.

Satisfy this, and the app workssrc/lib/commerce/types.ts

Seven services, one object. Every call site in the storefront and the admin goes through it, which is why an adapter is a new file rather than a fork.

export interface CommerceServices {
  products:   ProductService;
  carts:      CartService;
  quotes:     QuoteService;
  orders:     OrderService;
  discounts:  DiscountService;
  customers:  CustomerService;
  admin:      AdminService;
}

// one of the seven, in full:
interface ProductService {
  list(pageSize?: number, cursor?: PageCursor | null): Promise<ProductPage>;
  getById(id: string): Promise<Product | null>;
  search(term: string, maxResults?: number): Promise<Product[]>;
  create(product: Product): Promise<void>;
  update(id: string, data: Partial<Product>): Promise<void>;
  delete(id: string): Promise<void>;
  listFeatured(): Promise<Product[]>;
  feature(product: Product): Promise<void>;
  unfeature(featuredId: string): Promise<void>;
}
Where a call goes

One call site. Four places it can land.

The same line of storefront code reaches a bundled fixture, Firestore, or a SQL database. What changes is which adapter answers, and whether the answer comes from the browser or the server.

Any storefront or admin call

await commerce.products.list()

One typed interface CommerceServices

In the browser

  • Demounset

    src/lib/commerce/demo

    Bundled catalog. No network, no credentials.

  • Firebasefirebase

    src/lib/commerce/firebase.ts

    Reads direct from the browser; rules enforce access.

On the server

POST /api/commerce/[service]/[method]
Verifies the ID token, checks admin status, records the audit entry

  • Supabase / Prismasupabase · postgres

    src/lib/commerce/server/*.ts

    Privileged credentials never leave the server.

  • Admin writesany backend

    src/lib/commerce/server/firebase-admin.ts

    Admin writes, wherever the data lives.

Everything a real store needs, already wired.

Studio Headphones X1

Northsound

Studio Headphones X1

AUD-001

$129.99

Audio

Admin dashboard

Product CRUD, media, orders, and a quote pipeline.

Quotes and Stripe checkout

Guest or signed-in, with signature-verified webhooks.

Multi-language and multi-currency

Locale routing and live currency switching out of the box.

Inventory and roles

Stock counts, out-of-stock states, role-based admin access.

What holds it together

The parts that are easy to get wrong, already right.

A starter is only worth forking if its foundations survive contact with production. These are the ones that usually do not.

  • Checkout cannot oversell

    api/webhooks/stripe/route.ts

    The Stripe webhook writes the order and every stock decrement in one transaction, keyed by the session id. A retried delivery hits the same document; two concurrent ones cannot both spend the last unit.

  • Admin writes are audited

    lib/commerce/server/audit.ts

    Privileged mutations cross the server, which re-checks admin status and records who changed what with its own clock. An admin cannot skip or backdate the entry.

  • Prices come from the catalog

    api/checkout/route.ts

    The checkout route re-reads every product and variant server-side. Nothing the browser sends can move an amount.

  • Every push runs the gate

    .github/workflows/ci.yml

    Lint, types, the test suite, and a production build with no credentials at all. The demo backend is what makes that last one possible.

Open source. Self-hostable. Yours to fork.

git clone github.com/nebulanollie/ecommerce-boilerplate

Runs with no configuration. Connect a backend when you want real data.