Two repositories hold runnable code. Read them, or clone one and build on it.
Each app has a full tutorial of its own — see Example apps for the set. The two web stores are the Next.js storefront and the Remix storefront, the second of which also covers appointment booking.
| Repository | What it holds |
|---|---|
| appmint-examples | Complete apps — Next.js and Remix storefronts, plus Flutter demos |
| appmint-client | The client libraries themselves — TypeScript and Flutter |
Get the code
git clone https://github.com/JacLight/appmint-examples.git
cd appmint-examplesEach app is self-contained: its own dependencies, its own README, its own run command. There is no workspace to understand and nothing to build at the root — cd into the one you want and go.
appmint-examples/
nextjs-store/ a storefront: catalog, cart, server-priced totals
remix-store/ the same, plus reservations — no proxy needed
appmint_flutter_authentication_demo/ sign-in, magic link, sessions
appmint_flutter_chat_demo/ realtime chat over WebSocket
appmint_flutter_community_demo/ community features
appmint_flutter_crm_demo/ contacts and CRM records
appmint_flutter_events_demo/ events, tickets, check-in
appmint_flutter_payments_demo/ payments and checkoutJust want one app? Fetch a single folder rather than the whole history:
npx degit JacLight/appmint-examples/nextjs-store my-store
cd my-storeOr the Remix one:
npx degit JacLight/appmint-examples/remix-store my-store
cd my-storeRun the Next.js storefront
1. Install
cd nextjs-store
npm install2. Configure
cp .env.example .env.localAPPENGINE_ENDPOINT=https://appengine.appmint.io
ORG_ID=your-org-id
APP_ID=your-app-id
APP_KEY=your-app-key
APP_SECRET=your-app-secretYour org id and application credentials come from Studio Manager. .env.local is already in .gitignore — leave it that way.
3. Run
npm run devOpen http://localhost:4100. The catalog is your organization's own products, fetched server-side on first paint.
What to try
- Catalog → product → add to cart. Then open the cart: every figure there came from
storefront/pricing/calculate-cart. - Enter a promo code that does not exist. The total does not move, and a message explains why. That is a
200with a verdict, not an error. - Stop appengine, reload the cart. The total disappears rather than falling back to a locally summed number. That is deliberate — see below.
- Request
/api/repository/find/customer. You get404 Not proxied. The proxy forwards a storefront surface, not the whole API.
Reading the Next.js app
Six files carry the whole lesson.
| File | Why it matters |
|---|---|
lib/appmint.ts | The dual transport. request() picks by typeof window — browser to /api, server to appengine. The app token is fetched and renewed here, server-side only. |
app/api/[...slug]/route.ts | The proxy. An allow-list, the visitor's cookie read into x-client-authorization, upstream status codes preserved. |
lib/cart-storage.ts | The cart. sku, name, quantity — deliberately no money. |
components/CartView.tsx | The money path. Items, address and coupon in one call; the summary rendered field by field. |
app/page.tsx | Server-side catalog, showing calculatedPrice.finalPrice. |
app/product/[sku]/page.tsx | Detail, with list price struck through only when it differs. |
Three things the app will not do
These are the point of the example, and each one is a bug somebody has already shipped.
It never adds money up. No subtotal + shipping - discount anywhere. total already includes tax and shipping, so recomputing it drops the tax — and matches on tax-free destinations, which is why the bug survives review.
It never lets credentials near the browser. APP_SECRET is read in lib/appmint.ts only on the server path. The browser cannot reach appengine through this app at all.
It shows nothing rather than something wrong. When pricing fails there is no total. A plausible locally-summed figure is worse than a blank, because the customer believes it.
Build your own from it
The storefront is a starting point, not a framework. To turn it into yours:
- Replace
app/globals.css— the styling is deliberately plain. - Add routes for whatever else you sell. Anything without a helper on the client is one line:
appmint.get('storefront/collections'). - Extend the proxy allow-list in
app/api/[...slug]/route.tsas you reach new endpoints — and only as far as a visitor genuinely needs. - Add checkout.
orders.checkout()places the order; payment is configured per organization, so wire the gateway your org uses.
The client libraries
If you want the library rather than an app:
git clone https://github.com/JacLight/appmint-client.gitappmint-client/
appmint_js_client/ TypeScript — the one the Next.js store uses
appmint_flutter_client/ Dart/Flutter — device sessions, secure token storageThe TypeScript client is walked through end to end in Build a storefront with the TypeScript client.
Run the Remix storefront
cd remix-store
cp .env.example .env.local # same five values as above
npm install
npm run dev # http://localhost:4200Same catalog and cart, plus /book and /bookings — a complete guest
reservation flow: availability from the server, booking without an account,
lookup and cancellation by email. It is built from an empty folder, step by
step, in Tutorial: the Remix storefront.
One gotcha that is specific to Remix: Vite does not load .env.local into
process.env. The example calls dotenv.config() in vite.config.ts for
exactly this reason. Skip it and every credential is undefined, which shows up
as an empty catalog rather than an error.
The two are shaped differently on purpose. A web client puts your server between the browser and appengine, so credentials never ship. A mobile client talks to appengine directly — app credentials are readable from any binary, so there they are an identifier rather than a secret, and the signed-in person's token is what protects a request.
Other languages
There is a full OpenAPI document covering the API, served at documentation-json. For Java, C#, Python, Go or anything else, generate a client from it rather than writing one by hand — a generated client tracks the API as it changes.