
Northsound
Studio Headphones X1
AUD-001
$129.99
A production storefront, admin dashboard, and Stripe checkout in one repo. Swap the backend, keep the app.
Every storefront and admin call goes through a typed commerce module. Change the adapter, keep the app.
.env.local
NEXT_PUBLIC_COMMERCE_BACKEND=firebaseZero-config default. Firestore rules ship in the repo.
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>;
}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.
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.
A starter is only worth forking if its foundations survive contact with production. These are the ones that usually do not.
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.
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.
api/checkout/route.ts
The checkout route re-reads every product and variant server-side. Nothing the browser sends can move an amount.
.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.
git clone github.com/nebulanollie/ecommerce-boilerplateRuns with no configuration. Connect a backend when you want real data.