Stack
This page lists the packages and versions Litestore runs on and explains the design decisions behind them.
Litestore is built from public packages, and each one has its own documentation. Read this page when you want to know what a part of the app is built with, or before you upgrade a dependency.
None of these tools were invented for this project. The versions below are the ones declared in package.json, and they have been tested together. When you upgrade one, the harness tells you whether the upgrade broke an assumption.
Core framework
Next.js 16 (^16.1.1) provides the App Router. In development, it runs under Turbopack:
next dev -p 3000 --turbopackServer Components let a storefront page read the database directly. You don't have an API layer to keep in sync.
React 19 (^19.2.3) makes this approach practical in the browser. Actions and useOptimistic remove most of the client state a store would otherwise need.
TypeScript (^5.9.3) covers the whole repository, not just part of it:
bun run typecheckThis command runs tsc --noEmit across the repository. A schema change therefore breaks the build before it can break a checkout.
Zod 4 (^4.3.5) and zsa (0.6.0) validate input. Each module keeps its input schemas in the same predictable place:
server/<area>/<module>/schema.tsServer actions are wrapped so that validation and permission checks run before the mutation.
oRPC (@orpc/server ^1.13.9) provides the typed RPC API. It is defined in lib/orpc.ts, routed from server/router.ts and served at app/api/rpc/.
Database and caching
The database is PostgreSQL 15+, and its constraints enforce Litestore's guarantees. The baseline migration adds CHECK constraints such as these:
stock_movement_quantity_math
order_amounts_refunded_not_over_totalPrisma 7 (7.4.2) runs on top of Postgres. You get one schema file, a committed migration history in prisma/migrations/ and a fully typed client.
When Litestore creates the client, it extends it with the soft-delete filter from lib/soft-delete-extension.ts. The client connects through @prisma/adapter-pg.
Litestore uses Redis through Upstash (@upstash/redis, @upstash/ratelimit) for three jobs:
- the key-value helpers in
lib/cache.ts(getCache/setCache/deleteCache) - the rate limiters in
lib/rate-limit/ - the payment idempotency locks in
lib/rate-limit/payment-lock.ts
Payments and email
Stripe (^20.3.1) handles checkout, capture, refunds and disputes. All of these go through services/stripe.ts and lib/payments/payment-webhook.ts, and Litestore copies each one into the PaymentTransaction ledger.
Customers can browse and fill a cart without Stripe configured. Checkout needs it.
Resend (^6.9.2) and React Email send mail. The templates in emails/ are React components that @react-email/components renders.
People sign in with a magic link sent by email. You need RESEND_API_KEY to log in at all, and that includes the admin.
Infrastructure and runtime
Bun 1.2.2 is the declared packageManager, and it runs every script, including the harness checks. The application itself runs on Node.js 20+.
Uploads go to S3-compatible storage (@aws-sdk/client-s3, @aws-sdk/lib-storage). They pass through services/s3.ts into your own bucket, so storage stays on your bill.
Inngest (^3.54.0) runs the background work. Crons and event handlers are in functions/, and the same app serves them at app/api/inngest/.
functions/domain-events.ts takes each emitted domain event and turns it into cache invalidation and side effects.
Sentry (@sentry/nextjs ^10.40.0) is connected through four instrumentation files:
instrumentation.ts
instrumentation-server.ts
instrumentation-client.ts
instrumentation-edge.tsIf you leave the DSN unset, the app still runs, but you get no error reports.
Biome (^1.9.4) and Vitest (^4.0.18) are the development tools in the same package list.
bun run lintThis formats the code and runs the discipline checks.
bun run testThis runs Vitest.
bun run verifyThis runs the whole gate.
Authentication and styling
Better Auth (^1.4.19) is configured in lib/auth.ts with the magicLink and admin plugins. Sessions last 7 days.
Google sign-in is registered only when both AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET are set. If you self-host, you can run without it.
Tailwind v4 (^4.1.18) handles styling. The design tokens are declared in the @theme block of app/styles.css, so the admin and the storefront share one scale.
Radix UI primitives and @tanstack/react-table provide the unstyled behaviour under components/common/ and components/data-table/.
Architecture decisions
The stack follows from four decisions. Each one removes a moving part instead of adding one.
Litestore is a single deployable. The catalog and the cart run in one process. At this size, you have no network calls between microservices to manage.
The server decides the shape of the data. Server Components already fetch exactly what each page needs. Litestore therefore has no GraphQL layer, and clients can't ask for arbitrary shapes.
The storefront and the admin are one app. The storefront is not a separate project that calls an API. Without that split, you can't get bugs where something works in the admin but shows up wrong on the site.
You customize Litestore in your fork. There is no plugin sandbox, so you edit the code directly. The developer overview explains why a fork is the way to customize.
Versions
The versions in package.json are the tested ones.
When you upgrade Next.js or Prisma, the harness tells you whether the upgrade broke an assumption.
After an upgrade, it isn't enough to check that the app still starts. Ask this instead:
Which assumption did the new version change, and does the gate still pass?