Closest thing · Supabase, Firebase, PocketBase
Declare your data once and the rest arrives the same way it does on a hosted platform — CRUD endpoints, validation, row-level permissions, live updates, migrations, types. Except there is no platform. It is a package in your node_modules, an MIT license, and a SQLite file you own.
Closest thing · Laravel, Rails, Django
A queue, a scheduler, mail, notifications, policies, migrations, generators, one CLI — the things that make a framework feel finished rather than started. The difference is where they stop. In every one of those frameworks the model layer ends at the network boundary and the browser starts again from nothing. Here the same schema keeps going: the form knows the field rules and the button knows the permission, because both read the file the migration came from.
The last two are the whole argument. Everything above them you can already get.
Closest thing · the stack you already assembled
Nothing on the left is a bad choice. They are all good — they just never met. Each brings a config file, an error shape, a notion of a user and a separate idea of what your data looks like, and holding them in agreement is the job that never appears in a ticket.
around · before · after · error, API realm and UI realm// package.json
"prisma", // data
"zod", // validation, again
"casl", // authorization, again
"next-auth", // identity
"express", // transport
"socket.io", // realtime
"bullmq", // jobs
"node-cron", // schedule
"nodemailer", // mail
"react-email", // mail, again
"swagger-jsdoc", // the API, described again
"react", // ui
"react-hook-form", // the fields, again
"tailwindcss" // style
// + the glue between all fourteen// package.json
"@frontierjs/litestone", // Data
"@frontierjs/junction", // API
"@frontierjs/sierra", // UI
"@frontierjs/css" // style
// batteries, when you want them
"@frontierjs/auth"
"@frontierjs/caravan" // jobs + cron
"@frontierjs/notifications"
// the glue is the frameworkFewer dependencies is not the point. One vocabulary is the point — the count is just what it looks like from outside.
Closest thing · schema-first code generation
Generators are the good version of this idea: write a schema, get a client, a set of types, a scaffolded controller, an API description. Then you own the output. It drifts, you regenerate, you re-apply your edits, and the schema slowly stops being the truth.
Derivation has no output to own. Add one field and every one of these moves — because none of them was ever a copy.
// db/schema.lite
model Lead {
+ value Float (0)
}400 when value is negative/manifestdb.leadresource.fields.value — the browser's own rulemake() renders for it No generated/ directory. Nothing to review in the diff, nothing to re-apply, nothing that can be edited into disagreement with the schema.
Closest thing · Next, Nuxt, SvelteKit, Remix
Meta-frameworks start at the page and reach backwards until they touch the database, so the data layer ends up as whatever fits behind a route handler, and permissions get re-decided in each one.
FrontierJS starts at the schema and grows outward. The API is derived from it, the UI is bound to it, and the page is the last thing that happens — one of several surfaces, next to prerendered HTML, a transactional email, and a browser extension.
Page-first
The database is an implementation detail of the page. Every handler decides validation and permission again, and they agree until one doesn't.
Schema-first
Validation and permission are decided once, where the data is, and every surface inherits them. A page is one target on a list.
@frontierjs/css. Current home page →