API Reference Overview
The public Store REST API and the authenticated Commerce RPC API at a glance.
The boilerplate exposes two distinct API families, plus a handful of optional, feature-gated endpoints. Understanding which family you are talking to is the key to using the API correctly:
- The Store REST API (
/api/store/*) is a public, read-only, cache-friendly view of the catalog. It is meant for external clients — native apps, alternate frontends, integrations — that need to read products and categories without touching internal storage details. - The Commerce RPC API (
/api/commerce/*) is the authenticated backend the app itself calls when it runs on a SQL backend (Postgres/Prisma or Supabase). It is an internal transport, not a public API, and every non-public method requires a Firebase ID token.
The default quote-based store runs entirely on Firebase and mostly bypasses the Commerce RPC bridge (the client talks to Firestore directly). The RPC API matters when you swap in a SQL backend. See backends for how the commerce layer is wired.
The two API families
| Store REST API | Commerce RPC API | |
|---|---|---|
| Base path | /api/store | /api/commerce/<service>/<method> |
| Style | RESTful, resource URLs | RPC over POST |
| Method | GET (read-only) | POST |
| Auth | None (public) | Firebase Bearer token (public/user/admin) |
| Audience | External clients, native apps | The app's own client, SQL backends |
| Caching | CDN Cache-Control headers | None (force-dynamic, live data) |
| Version | v1 | Unversioned internal bridge |
| Writes | Never | Yes (carts, quotes, orders, admin) |
Store REST API
The Store API reshapes data that is already publicly readable (products,
categories) into a stable wire format so callers never depend on internal
storage details. Inventory counts, for example, are collapsed into an inStock
flag so exact stock levels are never exposed.
GET /api/store is a discovery document that lists the available endpoints and
the API version:
curl https://your-store.example.com/api/store{
"name": "Your Store",
"version": "v1",
"endpoints": [
"/api/store/products",
"/api/store/products/{id}",
"/api/store/categories"
]
}Key characteristics:
- Read-only. Every endpoint is a
GET. There are no public write paths. - Cursor pagination.
GET /api/store/productsreturns{ products, nextCursor, totalCount }; pass the returnednextCursorback as?cursor=to page forward. See Store: Products. - CDN cache headers. Responses carry a public
Cache-Controlheader so a CDN or the browser can cache them (details below). - Stable wire shape. Products are returned as a narrowed
PublicProduct(id,productName,brand,price,categories,description,primaryImageURL,pdfLink,inStock).
Commerce RPC API
POST /api/commerce/<service>/<method> is an RPC-style bridge from the client to
a server-side commerce adapter. The client posts one request per commerce
call:
POST /api/commerce/products/list
Authorization: Bearer <firebase-id-token>
Content-Type: application/json
{ "args": [] }and receives { "result": <return value> }, or { "error": string } with a
4xx/5xx status. Dates in the result serialize to ISO strings and are revived
by the client.
Access levels
Firebase Auth is the identity provider. Each whitelisted method carries one of three access levels:
| Level | Requirement |
|---|---|
public | No auth. Catalog reads and discount lookup. |
user | Valid token required; the method's uid argument (args[0]) must equal the token's uid, so a signed-in user can only act on their own cart/quotes/orders/profile. |
admin | Valid token required and admin.isAdmin(uid) must return true. |
quotes.submit is the one exception to the user rule: it allows a null
userId for guest submissions (no token needed then), but a non-null userId
must still match the token.
A method that is not on the whitelist responds 404, indistinguishable from a
non-existent route. See Commerce RPC for the full
whitelist and per-service argument shapes.
The Commerce RPC route runs on the Node.js runtime and requires a configured SQL
backend (getServerCommerce()); if the backend is not configured, every call
responds 500 with { "error": "Commerce backend is not configured." }.
Optional feature endpoints
These endpoints back optional, env-gated features. They keep working as no-ops
(or return 503) when their integration is not configured, so the default
quote-based store runs without any of them.
| Endpoint | Method | Feature | When unconfigured |
|---|---|---|---|
/api/checkout | POST | Stripe Checkout (Checkout) | 503 { "error": "checkout not configured" } without STRIPE_SECRET_KEY |
/api/webhooks/stripe | POST | Stripe order recording (Webhooks) | Inactive without Stripe configured |
/api/notifications/status | POST | Resend status emails (Notifications) | 200 { "skipped": true } without RESEND_API_KEY |
See environment variables for the flags that switch these on.
Common error shape
Both API families return errors as a single JSON object:
{ "error": "Human-readable message" }The Store API uses jsonError(status, message) and the Commerce RPC route uses
an equivalent error(message, status) helper, so the shape is identical across
the surface. Request data is never echoed back in error messages.
Standard status codes
| Status | Meaning |
|---|---|
400 | Bad request — malformed input (e.g. invalid cursor, unknown category, short search term, or a body that is not { args: [...] }). |
401 | Authentication required — a protected Commerce RPC method was called without a valid token. |
403 | Forbidden — token is valid but not permitted (acting on another user's data, or missing admin privileges). |
404 | Not found — no such product, or an unknown Commerce RPC service.method. |
429 | Too many requests — rate limit hit (checkout and notifications endpoints). A Retry-After header is included. |
500 | Internal server error — unexpected failure, or the Commerce backend is not configured. |
503 | Service unavailable — an optional feature (e.g. Stripe checkout) is not configured. |
Cache-Control policies
The Store API sets one of two Cache-Control headers depending on how quickly
the underlying data changes. Both are defined in src/lib/api/store.ts:
/** Cache-Control for product endpoints (list, search, detail). */
export const PRODUCT_CACHE_CONTROL =
"public, s-maxage=60, stale-while-revalidate=300";
/** Cache-Control for slow-changing config-backed endpoints (categories). */
export const CONFIG_CACHE_CONTROL =
"public, s-maxage=3600, stale-while-revalidate=86400";| Policy | Header | Applied to |
|---|---|---|
PRODUCT_CACHE_CONTROL | public, s-maxage=60, stale-while-revalidate=300 | /api/store/products, /api/store/products/{id} (product list, search, and detail) |
CONFIG_CACHE_CONTROL | public, s-maxage=3600, stale-while-revalidate=86400 | /api/store (discovery) and /api/store/categories |
s-maxage is the shared-cache (CDN) freshness window in seconds;
stale-while-revalidate lets a CDN serve stale content for that many additional
seconds while it revalidates in the background. Products stay fresh for 60s;
category and config data, which changes rarely, for an hour.
The Commerce RPC route is declared force-dynamic and sets no cache headers —
every RPC call resolves live data.