# Scaffold with create-rindle

Generate and run a TanStack Start app with SQL migrations, a synced browser client, an API server, and SSR.

Build an app with live messages, optimistic writes, and a shared database.
`create-rindle` generates the browser client, API server, and local data tier in a
[TanStack Start](https://tanstack.com/start) project.

This guide starts the app on your computer. You will see a message sync between
two browser windows, then change the generated project.

Use this starter when you want a new synced TypeScript app with routing and server
rendering included. To add sync to another framework, use the
[manual quickstart](https://rindle.sh/docs/synced-app-quickstart). For local queries without a server,
start with the [browser engine](https://rindle.sh/docs/wasm-client). For remote reads or normalized
sync without prediction, compare the [browser clients](https://rindle.sh/docs/browser-clients).
For an embedded Rust database, use [`rindle-replica`](https://rindle.sh/docs/replica-and-views).

## Before you start

You need Node.js 22.18 or later and npm or pnpm. The starter uses TypeScript and
React. It includes the local Rindle binaries and requires no cloud account.

## 1. Create the project

With pnpm, run:

```bash
pnpm create rindle my-app
cd my-app
pnpm dev
```

With npm, run:

```bash
npm create rindle@latest my-app
cd my-app
npm run dev
```

The generator installs dependencies with your package manager. The development
command starts the app at [localhost:3000](http://localhost:3000).

## 2. See live updates

1. Open [localhost:3000](http://localhost:3000) in two browser windows.
2. Create a room in the first window.
3. Open the room in both windows.
4. Post a message in the first window.

The message appears immediately in the first window and syncs to the second.
The room's message count updates with each message.

To see a rejected write, create a room named `spam`. The room appears briefly,
then disappears when the API server rejects it. A toast explains the rejection.

The starter includes a development identity so you can write immediately.
Before production, replace it with your own authentication provider.

## 3. Understand the development command

The generated `dev` script runs one lifecycle command:

```bash
rindle dev --migrate --gen shared/schema.gen.ts -- vite dev --port 3000
```

`rindle dev` reads `rindle.ncl` and starts the local write master, follower, and
fleet edge. The master accepts database writes. The follower serves live queries,
and the edge routes requests to them. The command applies migrations and generates
`shared/schema.gen.ts`. Then it starts TanStack Start with `RINDLE_URL` and
`RINDLE_DATABASE_TOKEN`.

The database token stays on the server. The browser calls the app's
`/api/rindle/*` routes. An authorized subscription response, called a **lease**,
gives it the endpoint for live updates.

## What it generates

The starter generates the same three tiers as the manual quickstart:

| Tier | In the template | What it does |
| --- | --- | --- |
| Browser | `src/rindle-client.ts`, `src/rindle-tanstack.ts`, `src/components/*.queries.ts`, `src/routes/*` | Displays live query results and predicts writes immediately |
| API authority | `server/app-api.ts`, `server/rindle-http.ts`, `src/routes/api.rindle.*.tsx` | Checks access, resolves named queries, and runs authoritative mutators |
| Data tier | `rindle.ncl`, `migrations/*.sql` | Stores data, applies migrations, and sends live query changes |

The schema is SQL-first. Edit or add `migrations/*.sql`. The dev loop applies the
migration to the write master and regenerates `shared/schema.gen.ts` from the follower's
introspected schema. A file can be pure DDL (including reviewed destructive drops) or
pure DML for seeds and bounded backfills. Keep the two kinds in separate ordered files.
Keep relationships, normalization, and mutators in `shared/app-def.ts`.

## Why TanStack Start

The starter uses TanStack Start because it gives the template one home
for the browser, SSR, and server routes:

- `src/routes/api.rindle.query.tsx`, `api.rindle.read.tsx`, and
  `api.rindle.mutate.tsx` are the browser-facing API routes.
- [SSR reads](https://rindle.sh/docs/ssr) call the same app authority in-process, so first paint
  and client subscriptions use the same query registry.
- `@rindle/tanstack` lets each file route declare its query once, then owns
  server preloading, client navigation readiness, cancellation, and the
  seed-to-live provider handoff.
- TanStack Router's file routes keep app screens and Rindle query modules close
  without forcing the Rindle APIs themselves to depend on TanStack.

This template chooses the optimistic client and a replicated data tier.
Other Rindle integrations do not require that combination. TanStack Start is
the template's application framework; the query engine and client APIs also
work outside it.

## Where to look first

| File | Why it matters |
| --- | --- |
| `migrations/0001_init.sql` | The source-of-truth SQL schema |
| `shared/schema.gen.ts` | Generated `@rindle/client` schema; do not hand-edit |
| `shared/app-def.ts` | Schema re-export, relationships, normalization, and predicted mutators |
| `src/components/*.queries.ts` | Named root queries and fragments, co-located with UI |
| `src/rindle-client.ts` | The one browser client setup call |
| `src/rindle-tanstack.ts` | The shared route-loader/provider integration |
| `src/ssr.ts` | Server-only first-paint preload helper |
| `src/devtools.tsx` | Dev-only Rindle devtools panel mount |
| `server/app-api.ts` | Authoritative query registry, SQL mutators, policy |
| `rindle.ncl` | The topology `rindle dev` (local) and `rindle deploy` (cloud) both read — `followers = 1`, the colocated pair; loopback in dev |
| `AGENTS.md` | App instructions for coding agents, including generated files, mutators, and named queries |

## Customize it

Treat the generated app as a working baseline:

- Change tables in `migrations/*.sql`, then let `pnpm dev` regenerate the typed
  schema.
- Add relationships and mutators in `shared/app-def.ts`.
- Add named root queries or fragments beside the components that read them.
- Replace the demo auth seam in `shared/auth.ts` and the policy in
  `server/app-api.ts`.
- If you point the app at Rindle Cloud or a self-hosted fleet, keep
  `RINDLE_DATABASE_TOKEN` server-only.

The same `rindle.ncl` drives the cloud:

1. Run `rindle login`.
2. Run `pnpm rindle:deploy`. It provisions (or re-attaches) the **Sync** plan — a
   managed master + follower on Rindle's packed OVH fleet — and records the binding
   in `.rindle/cloud.json`. Commit that file.
3. Run `pnpm rindle:migrate:cloud` to push `migrations/*.sql` through the Cloud proxy.

Local dev reads the same authored topology through `rindle dev`. See
[deploy](https://rindle.sh/docs/deploy).

For the underlying commands, see [`@rindle/cli`](https://rindle.sh/docs/rindle-cli). For the
manual version of the same app shape, keep the [quickstart](https://rindle.sh/docs/synced-app-quickstart)
open beside the generated project.

## Next steps

- [Synced-app quickstart](https://rindle.sh/docs/synced-app-quickstart) - the same architecture,
  built by hand.
- [The three-tier architecture](https://rindle.sh/docs/architecture) - why the browser, API
  authority, and daemon are separated.
- [`@rindle/cli`](https://rindle.sh/docs/rindle-cli) - the `rindle` toolchain the template uses
  for the daemon, migrations, and schema generation.
- [The browser client](https://rindle.sh/docs/client) - `createRindleClient`, optimistic mutators,
  and live reads.
- [Server rendering](https://rindle.sh/docs/ssr) - the first-paint preload and live-store handoff.
- [Devtools](https://rindle.sh/docs/devtools) - the dev-only mutation timeline, query inspector,
  and delta stream.

---

[View this page on Rindle](https://rindle.sh/docs/create-rindle)
