Skip to content
Flare 0.8.0: money, percent, rating, timezone, currency and seven more field types.See them all

Choosing a stack

Flare’s job is to turn a resource descriptor into a database table, a REST API, validators and a dashboard. Where that runs is a separate question, and there are two answers.

Terminal window
npx flare create myapp # Cloudflare Workers (default)
npx flare create myapp --stack next # Next.js on Vercel
Cloudflare (default) Next.js
Framework vinext (Next-compatible RSC) Next.js 16, App Router
Runs on Cloudflare Workers Vercel
Database D1 (SQLite) Neon (serverless Postgres)
ORM Drizzle Prisma 7
Cache Workers KV Upstash Redis
Files R2 R2 or UploadThing
Auth Better Auth Better Auth
Email Resend Resend + React Email
Payments Stripe Stripe
Styling Tailwind v4 + shadcn/ui Tailwind v4 + shadcn/ui

Auth, email, payments, styling and the dashboard are the same on both. What changes is the runtime, the database and the ORM.

Cloudflare if cost and reach matter most. It is the cheaper of the two by a distance — $5/month covers a great deal, R2 charges nothing for egress, and the app runs in every Cloudflare location without you arranging it. The trade-off is SQLite: D1 is a real database but it is not Postgres, and the Workers runtime is not Node.

Next.js if you need Postgres or Node. Real Postgres with extensions, window functions and full-text search; any npm package that assumes Node; Vercel’s preview deployments; and a much larger pool of people who have worked in exactly this stack before. It costs more — Vercel, Neon and Upstash each have their own bill — and you give up R2’s free egress unless you keep files there.

If you are unsure, start on Cloudflare. Costs has the real numbers, and your descriptors, hooks, policies and components move between the two.

Shared, because it is ordinary TypeScript with no runtime in it:

  • Resource descriptors, hooks, computed fields
  • Policies and roles
  • Every React component in the dashboard
  • Seeds, and the field type grammar

Generated per stack:

  • The schema and migrations — Drizzle/SQLite or Prisma/Postgres
  • The route handlers — Workers or Next.js Route Handlers
  • The database client and the cache adapter

This is the whole difference, in the files one flare gen resource Product writes. Everything else in the app is the same code.

resources/product.resource.ts
export default defineResource({
name: "Product",
fields: {
name: field.string(),
sku: field.string({ unique: true }),
price: field.float(),
category: field.belongsTo("Category", { required: false }),
},
});

The schema — different language, same shape

Section titled “The schema — different language, same shape”
Cloudflare — db/schema/products.ts
export const products = sqliteTable("products", {
id: text("id").primaryKey(),
name: text("name").notNull(),
sku: text("sku").notNull().unique(),
price: real("price").notNull(),
categoryId: text("category_id").references(() => categories.id, { onDelete: "set null" }),
createdAt: integer("created_at").notNull(),
updatedAt: integer("updated_at").notNull(),
});
Next.js — prisma/schema/resources.prisma
model Product {
id String @id @default(uuid())
name String
sku String @unique
price Float
categoryId String? @map("category_id")
category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("products")
}
app/api/products/route.ts
import { createResourceHandlers, drizzleRows } from "@/lib/resource";
// …
rows: drizzleRows(products, getDb), // Cloudflare
rows: prismaRows(prisma.product, prisma), // Next.js

Both call the same createResourceHandlers — which is code in your app, not in a package. The row adapter is the seam, and it is about 130 lines on either side.

Cloudflare Next.js
Write a migration flare gen migration --from-schema prisma migrate dev
Apply it flare migrate flare migrate (= prisma migrate deploy)
Dev server flare dev flare dev (= next dev)
Deploy flare deploy flare deploy (= vercel deploy --prod)
Fill a table flare seed:resource Product 25k flare seed:make + flare seed

Beyond the table at the top, these are the differences that change what you can build.

The runtime. Workers is not Node. Most npm packages work; anything using Node’s filesystem, native modules or long-lived TCP does not. Next.js on Vercel runs Node, so everything works.

CPU per request. A Worker on the free plan gets 10ms of CPU per invocation, and 30 seconds on the paid one — plenty for a query and a render, not enough to resize video. Vercel functions run to their configured timeout.

SQLite versus Postgres. D1 is real SQL and fine for most applications. Postgres gives you extensions, window functions, tsvector full-text search with ranking, jsonb operators and materialised views. If you know you need one of those, the choice is made.

Cold starts. Workers have effectively none. Neon’s free tier suspends an idle database, so the first request after a quiet spell waits for it to wake — a plan choice, not a code problem.

Egress. R2 charges nothing for bandwidth out. For an app that serves files, this is often the single biggest line on the bill, which is why both stacks use R2 for storage.

Realtime. Cloudflare gives every channel a Durable Object — one address, its own storage, websockets that stay open. Vercel has no equivalent, so realtimeChannel().publish() is a no-op on that stack.

Not with a command, and there isn’t one planned. What moves without change: your descriptors, hooks, computed fields, policies, seeds, every dashboard component, and lib/resource/ itself apart from its row adapter. What doesn’t: the schema and its migration history, the database client, the cache adapter and the deployment.

In practice, moving a small app is an afternoon — regenerate the resources on a new app of the other stack and move the data. Moving a large one with a year of migrations is not.

The honest advice is to pick for the database. Everything else is either shared already or a day’s work; a schema with real data in it is neither.

Terminal window
npx flare create shop --stack next
cd shop
cp .env.example .env # DATABASE_URL from Neon, BETTER_AUTH_SECRET
npx flare gen resource Product --fields 'name:string, sku:string!, price:float'
npx prisma migrate dev # turns the schema into a migration and applies it
npm run dev

flare gen resource writes the same route handlers, typed client, validators and dashboard pages it writes on Cloudflare. What changes is the schema: prisma/schema/resources.prisma instead of a Drizzle module per table.

prisma/schema is a folder of two files, and the split matters:

  • base.prisma — the generator, the datasource, and the tables Better Auth and the dashboard need. Yours to edit; never regenerated.
  • resources.prisma — your resources, written from the descriptors. Overwritten every time you run flare gen resource.

Migrations are Prisma’s: npx prisma migrate dev diffs the schema against your database and writes the SQL. flare migrate applies what is already written — it runs prisma migrate deploy here — which is the one you want against a database that matters, because it never invents a migration.

flare dev, build, start and deploy work here too, delegating to Next and the Vercel CLI instead of vinext and wrangler — so the commands are the same on both stacks even though what they run isn’t. flare build runs prisma generate first, and flare deploy is vercel deploy --prod with your arguments forwarded. Deploying to Vercel is the detail.

Being straight about it:

  • Realtime. Cloudflare gives every channel a Durable Object — one address, its own storage, websockets that stay open. Vercel has no equivalent, so realtimeChannel().publish() is a no-op. A hosted pub/sub behind the same two functions is the way to add it.
  • Traffic analytics in-app. The observability page reads Cloudflare’s analytics API on the other stack; here it points you at Vercel’s dashboard rather than holding a token that can read your whole account.
  • flare seed:resource. The one-liner that fills a table from its descriptor writes to D1 through wrangler, so it is Cloudflare-only. Seed files themselves work: flare seed:make <name> --resource Product writes one with the rows filled in, and flare seed runs it. What it hands you is the app’s own Prisma client, plus an insertMany over createMany.