Tutorial: a contact app on Next.js
Thirty minutes, one resource, and every layer of a Flare app: the database,
the REST API, the admin screens, sign-in and roles. Everything below is a
flare command — you will not run Prisma, and you will not write SQL.
You need Node 22+, and Docker Desktop running (it stands in for Postgres; a Neon connection string works too).
1. Create the app
Section titled “1. Create the app”pnpm create flare-framework contactscd contactsIt asks five questions — where it runs, where uploads go, the look, sign-in methods, social providers. Press Enter through all of them: the defaults are Next.js, R2 against local MinIO, email-and-password sign-in.
2. Start the database, the tables, and yourself
Section titled “2. Start the database, the tables, and yourself”docker compose up -d # Postgres, on the address .env already hasnpx flare migrate # create the tablesnpx flare db:seed # an account to sign in withnpx flare dev # http://localhost:3000Open localhost:3000 and sign in as admin@example.com / Password. You’re looking at the dashboard: a Users screen with your account in it, observability, and an overview telling you the three commands you’re about to run.
3. Your first resource
Section titled “3. Your first resource”From a second terminal in the app:
npx flare gen resource Contact --fields 'name:string, email:email!, status:enum(lead,customer)'npx flare migrate # create its tablenpx flare seed:resource Contact 1000Refresh the dashboard. Contacts is in the sidebar with a thousand rows —
search, filters, sorting, paging, CSV export, a form that already validates
email and only offers lead or customer for status. The same descriptor
produced a REST API:
curl http://localhost:3000/api/contactsand documented it at /api/openapi.json.
Nothing is hidden: the descriptor is resources/contact.resource.ts, the route
handlers are in app/api/contacts/, the screens in app/dashboard/contacts/ —
all yours to edit.
4. Change your mind about a field
Section titled “4. Change your mind about a field”Rename name to fullName without losing the thousand rows. In
resources/contact.resource.ts:
fullName: field.string({ rename: { from: "name" } }),npx flare gen resource Contact # regenerate from the descriptornpx flare gen migration rename_contact --from-schema # writes ALTER TABLE ... RENAME — "data kept"npx flare migrateThe declared rename is why that second command wrote an
ALTER TABLE … RENAME COLUMN rather than what Prisma would do on its own —
drop the column and add a new one, taking the data with it. Check the
dashboard: the column is Full Name and all thousand rows are still there.
5. Decide who may do what
Section titled “5. Decide who may do what”npx flare gen policy Contact --roles admin,staffNow only admin and staff can touch contacts — in the dashboard and
over the API, same rule, one file (policies/contact.policy.ts). Try it:
make a second account from Users → New user with the role user, open a
private window and sign in as them. No Contacts in the sidebar, 403 from
the API. Change their role to staff from the row menu, and it all appears.
6. Where to go next
Section titled “6. Where to go next”- Uploads — add
avatar:imageto the fields and regenerate; see file uploads. - Deploy — a Neon database,
npx flare migrate --deployagainst it, andnpx flare deployto Vercel; the walk-through is deploying to Vercel. - A bigger build — the catalogue tutorial takes this stack further; the shop with a till does the same on Cloudflare.
The same app on the other stack is a contact app on Cloudflare — the commands are identical from step 3 on, which is rather the point.