FrontierJS
Skip to content
Hero AAnchor: Supabase / Firebase / PocketBase — “but it's your code.”Reaches the biggest audience. Risk: sets an expectation of a dashboard.

Closest thing · Supabase, Firebase, PocketBase

A backend that derives itself. Without the backend company.

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.

Everything you came for

  • Schema → REST API, no handler written
  • Validation and 400s derived from the field rules
  • Permissions on the data itself, compiled into every query
  • Realtime on mutation, over WebSocket channels
  • Migrations diffed from the schema
  • Generated types, server and browser

None of the tradeoffs

  • No account, no dashboard, no phone-home
  • No second place your business rules live
  • No SDK ceiling — outgrow the derived API and you write a real service with hooks, in the same file tree
  • No egress bill, no seat pricing, no region
Hero BAnchor: Laravel / Rails — “but it doesn't stop at the server.”Best ecosystem signal. Risk: invites a maturity comparison FJS loses.

Closest thing · Laravel, Rails, Django

That kind of complete. Except it doesn't stop at the server.

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.

If you know one, you know this

Artisan→fli
Eloquent / ActiveRecord→litestone
Migrations→diffed from schema.lite
Policies / Pundit→@@gate · @@allow
Form requests / validation→derived from fields
Queues · Scheduler→app.jobs
Mail · Notifications→app.notify
Broadcasting→app.channel
Blade / ERB→.mesa
no equivalent — validation rules in the browser→resource.fields
no equivalent — the model's permissions in the browser→resource.can()

The last two are the whole argument. Everything above them you can already get.

Hero CAnchor: the reader's own package.json — “but they were designed together.”Most concrete. Risk: names real packages; keep it about shape, not quality.

Closest thing · the stack you already assembled

The same stack. Designed together.

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.

  • One schema — the ORM, the API validation and the form rules read the same file
  • One hook pipeline — around · before · after · error, API realm and UI realm
  • One error boundary — every package's errors map to a status the same way
  • One CLI — scaffold, migrate, run, build, deploy

Today

// 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

Instead

// 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 framework

Fewer dependencies is not the point. One vocabulary is the point — the count is just what it looks like from outside.

Hero DAnchor: Prisma generate / OpenAPI codegen / Rails scaffolds — “but nothing is generated.”Sharpest idea, narrowest audience. Needs a reader who has maintained generated code.

Closest thing · schema-first code generation

Schema-first. With nothing generated.

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)
}

What just changed, with no command run

  • db the migration diff
  • api the 400 when value is negative
  • api the JSON Schema at /manifest
  • api the TypeScript type on db.lead
  • web resource.fields.value — the browser's own rule
  • web the input make() 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.

Hero EAnchor: Next / Nuxt / SvelteKit — “but it grew the other way.”Directly answers “another Next starter?”. Risk: picks a fight with the incumbent.

Closest thing · Next, Nuxt, SvelteKit, Remix

Same territory. Grown from the other end.

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

Page→Route handler→ORM→Database

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

schema.lite→Model→Service→Resource
└→SPAStatic + islandsEmailExtension

Validation and permission are decided once, where the data is, and every surface inherits them. A page is one target on a list.

Five candidates — pick the audience, and the differentiator follows. Not a page to ship as-is.Styled entirely by @frontierjs/css. Current home page →