Litestore · Developer guide

Stack

Learn which versions of Next.js, React, Prisma, Tailwind, Better Auth, Stripe and Inngest Litestore runs on, and the architecture decisions behind them.

Every dependency here is a public package with its own documentation. Nothing on this list is invented for this project.

The versions below are the ones declared in package.json. They are the combination that has been tested together, and the harness is what tells you whether an upgrade broke an assumption.

Core framework

Next.js 16 (^16.1.1) supplies the App Router. Development runs it under Turbopack:

next dev -p 3000 --turbopack

Server Components let a storefront page read the database directly, so there is no API layer to keep in sync.

React 19 (^19.2.3) is what makes that practical on the client side. Actions and useOptimistic remove most of the client state a store would otherwise carry.

TypeScript (^5.9.3) covers the repository rather than a subset of it:

bun run typecheck

Runs tsc --noEmit across the repository, so a schema change breaks the build before it breaks a checkout.

Zod 4 (^4.3.5) and zsa (0.6.0) handle input. Every module's input schemas live in one predictable place:

server/<area>/<module>/schema.ts

Server actions are wrapped so that validation and permission checks run before the mutation does.

oRPC (@orpc/server ^1.13.9) is the typed RPC surface. It is defined in lib/orpc.ts, routed from server/router.ts and served at app/api/rpc/.

Database and caching

PostgreSQL 15+ is the database, and the constraints are where the guarantees live. The baseline migration adds CHECK constraints such as:

stock_movement_quantity_math
order_amounts_refunded_not_over_total

Prisma 7 (7.4.2) sits on top of it: one schema file, a committed migration history under prisma/migrations/, and a fully typed client.

The client is extended at construction with the soft-delete filter in lib/soft-delete-extension.ts, and runs over @prisma/adapter-pg.

Redis via Upstash (@upstash/redis, @upstash/ratelimit) backs three things:

  • 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, refund and dispute. They all flow through services/stripe.ts and lib/payments/payment-webhook.ts, and are mirrored into the PaymentTransaction ledger.

The store browses and carts without Stripe configured. Checkout needs it.

Resend (^6.9.2) and React Email deliver mail. Templates in emails/ are React components rendered with @react-email/components.

Sign-in uses magic links delivered by email, so RESEND_API_KEY is required to log in at all — including into the admin.

Infrastructure and runtime

Bun 1.2.2 is the declared packageManager and the runner for every script, including the harness checks. The application itself runs on Node.js 20+.

S3-compatible storage (@aws-sdk/client-s3, @aws-sdk/lib-storage) handles uploads. They go through services/s3.ts against your bucket, so storage stays on your bill.

Inngest (^3.54.0) runs the background work. Crons and event handlers live in functions/ and are served from the same app at app/api/inngest/.

functions/domain-events.ts is what turns an emitted domain event into cache invalidation and side effects.

Sentry (@sentry/nextjs ^10.40.0) is wired through four instrumentation files:

instrumentation.ts
instrumentation-server.ts
instrumentation-client.ts
instrumentation-edge.ts

Leaving the DSN unset does not stop the app from running, but you get no error telemetry.

Biome (^1.9.4) and Vitest (^4.0.18) are the tooling side of the same package list.

bun run lint

Formats and runs the discipline checks.

bun run test

Runs Vitest.

bun run verify

Runs the whole gate.

Authentication and styling

Better Auth (^1.4.19) is configured in lib/auth.ts with the magicLink and admin plugins and a 7-day session.

Google sign-in registers only when AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET are both set, so a self-hoster can run without it.

Tailwind v4 (^4.1.18) provides the styling layer. 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 supply the unstyled behaviour underneath components/common/ and components/data-table/.

Architecture decisions

The stack above is a consequence of four decisions, each of which removes a moving part rather than adding one.

Litestore is a single deployable. The catalog and the cart run in one process, with no network hops between microservices to manage at this size.

The server decides data shapes. Server Components already fetch exactly what each page needs, so there is no GraphQL layer — the server decides the shape instead of a client asking for arbitrary ones.

The storefront and the admin are one app. The storefront is not a separate project consuming an API. Removing that seam removes the entire class of "works in the admin, wrong on the site" bugs.

Customization goes through your fork. There is no plugin sandbox; you edit the code directly — see the developer overview for why a fork is the customization model.

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.

The useful question after an upgrade is not whether the app still starts. It is:

Which assumption did the new version change, and does the gate still pass?

On this page