Skip to content

Quickstart

From nothing to a deployed CRUD app: a scaffolded project, a resource with a working REST API and admin screens, and a live URL. No file in this loop is hand-written.

Pick a stack below and the whole page follows it — including the deploy section at the end.

Two other ways through this material: the command builder turns your choices — stack, storage, theme, sign-in — into the one command to copy; and the step-by-step tutorials build a contact app with more explanation than this page stops for, on Next.js or on Cloudflare.

Postgres, S3 and Redis are real services on this stack, so they have to come from somewhere. That is the only decision here.

Three accounts, all on free tiers. Create them first:

  1. Postgres — a project on Neon. Copy the pooled connection string.
  2. Files — an R2 bucket and an API token. Only needed for uploads; skip it until you add a file field.
  3. Redis — optional. Rate limits fall back to in-memory without it, which is fine on one machine. For the real thing, an Upstash database.
Terminal window
pnpm create flare-framework myapp
cd myapp
# paste DATABASE_URL into .env, plus the R2_* keys if you want uploads
npx flare migrate # create the tables
npx flare db:seed # an account to sign in with
npx flare dev # http://localhost:3000

Then your first resource, from a second terminal in the app:

Terminal window
npx flare gen resource Contact --fields 'name:string, email:email!, status:enum(lead,customer)'
npx flare migrate # create its table
npx flare seed:resource Contact 1000

flare create already wrote the .env with a fresh BETTER_AUTH_SECRET, so DATABASE_URL is the only line you have to fill in before flare migrate.

You now have this running on http://localhost:3000:

Where What
/ the app
/dashboard the admin, with a Contacts screen that sorts, filters, pages and exports, and a Users screen from day one
/api/contacts the REST API — GET, POST, and GET/PATCH/PUT/DELETE on /api/contacts/[id]
/api/openapi.json an OpenAPI 3.1 document, generated from the same descriptor

Sign up at /dashboard. The first account to sign up becomes the admin, so the Contacts screen is there immediately — a new resource is admin-only until you widen its policy.

flare create scaffolded a working app before any resource existed: sign-in and sign-up screens, a session, a database, an admin shell, and the bindings. Add --auth-providers google,github to scaffold OAuth buttons too; they only render once you set the matching client ID and secret.

flare gen resource read one resource descriptor and emitted everything it implies — about nineteen files, all into your repository:

  • the schema and a migration — Prisma and Postgres, or Drizzle and SQLite
  • Zod validators, strict, so there is no mass assignment
  • the REST API, with auth and the policy already on it
  • a typed fetch client (resources/contact.client.ts)
  • list, create, edit and detail screens, with loading skeletons shaped like each one
  • an admin-only policy beside the descriptor

See what gen resource emits for the full list, and the field grammar for every type you can put in --fields.

flare migrate applied that migration. The SQL is a file in your repo and reads like one you wrote.

Vercel owns the build and the environment, so flare deploy is the Vercel CLI and migrations stay a separate, deliberate step:

Terminal window
# once: point DATABASE_URL at the production database and migrate it
DATABASE_URL="<your production connection string>" npx flare migrate
npx flare deploy

Set the same environment variables in the Vercel project — DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, and the R2_* keys if you use uploads. Vercel builds do not run migrations, which is why the first command is there: a deploy onto an empty database is a 500 on your first sign-up.

The Vercel guide covers preview databases and the shadow-database error prisma migrate dev gives on serverless Postgres.

The first account is already an admin. To grant a second person a role:

Terminal window
npx flare user:role them@example.com admin

It writes to whichever database DATABASE_URL points at, so set it to production when you mean production.

Every generated resource arrives admin-only. Widen one with flare gen policy Contact --roles admin,staff, or open it to everybody with --roles '*'. See roles and policies.

  • email/password auth end to end, with sessions
  • a Contact resource with a real REST API, validation and an OpenAPI document
  • an admin dashboard that sorts, filters, pages and exports
  • a cache invalidated automatically on every write
  • a live URL

No file in this loop was hand-written, and none of it is hidden: the engine was copied into your app, not imported from a package you cannot read. Edit the descriptor and re-run gen resource whenever you like — the codegen overwrite contract says exactly what survives a re-run, and flare diff shows what you have changed.