BookbagBookbag
Engineering

I Built Master Because I Missed the Feeling of Rails

A few years ago, building web apps felt simpler. You generated a model, a controller, a few routes, connected a database, and shipped something. Here is why I built Master to get that feeling back without leaving the Node.js world.

The Bookbag Team·June 2026· 14 min read
A glowing command-line prompt scaffolding a full application stack upward into connected panels and nodes, on a dark indigo background.

A few years ago, building web apps felt simpler. You had an idea, you generated a model, you generated a controller, you added a few routes, you connected a database, and you shipped something. It was not always pretty, but the framework was clearly trying to help you move.

Lately, starting a full-stack JavaScript app feels different. The ecosystem is more powerful than it has ever been, and that is exactly the problem. Before you write a single line of product code, you are staring down a pile of decisions. Which backend framework? Which ORM? Where do migrations live? How does the API talk to the frontend? One repo or three? How much wiring do you tolerate before you get to build the thing you actually set out to build?

I have lost whole evenings to that pile. So I built Master, a full-stack framework for Node.js that hands you a real application structure from the first command. The rest of this post is the honest version of why, what it does, where it fits, and where I want a fight about the design.

  • Which backend framework should I use?
  • Which ORM, and how do migrations work with it?
  • How should the API connect to the frontend?
  • Monolith, separate services, or something in between?
  • How much boilerplate do I owe before I can build the actual product?
What is Master?

Master is an open-source full-stack framework for Node.js. One command scaffolds a Next.js frontend, a MasterController API backend, and the MasterRecord ORM, wired together and ready to run. The goal is Rails-style productivity in modern JavaScript, without leaving the Node ecosystem.

Why I built it

I built Master because I kept rebuilding the same foundation. I have shipped a lot of web apps: small side projects, internal tools, a couple of things people pay for. Most of them needed the same scaffolding underneath. Authentication. CRUD screens. An API. Background logic. A database that changes shape as the product changes. Every new app started with the same week of plumbing before any of it became interesting.

I did not want a tiny router and a blank folder, where freedom mostly means you get to make a hundred small architecture decisions that you will second-guess in three months. I also did not want a giant black box that does everything until the day you need it to do something slightly different, and then you are spelunking through source you never chose to depend on.

There is a real cost to the blank-folder approach, and it compounds. The first decision is cheap. By the tenth, you are no longer building a product; you are designing a personal framework, badly, under deadline, and every junior who joins inherits your one-off choices with no documentation. The promise of total flexibility quietly becomes the tax of total responsibility. I had paid that tax enough times to want a default I trusted instead.

What I wanted sat in the middle: opinionated enough to make me fast, modular enough that I could open any layer, understand it, and replace it. That is the whole philosophy. Three pieces, one framework, each useful on its own and better together. Opinions where opinions save time, seams where you might genuinely want to do something else.

The framework should make the first hour boring, so the next hundred can be interesting.

The design goal behind Master

The three pieces of Master

Master is three tools that share conventions: a CLI, a backend framework, and an ORM. You can reach for them together through one scaffold, or pull a single one into an existing project. The split matters because it keeps each layer honest. Nothing has to know more about the others than it should.

Here is what each piece owns.

PieceWhat it isWhat it handles
Master CLIThe command centerCreating apps, generating resources, running dev, building, starting, and managing database workflows
MasterControllerThe backend frameworkRouting, controllers, middleware, WebSockets, CORS, CSRF protection, rate limiting, secure headers, and HSTS
MasterRecordThe ORMCode-first models, migrations, relationships, transformers, and support for SQLite, MySQL, and PostgreSQL

Why split it into three

Bundling everything into one inseparable runtime is how frameworks become hard to leave. Keeping the CLI, the backend, and the ORM as distinct tools means you can adopt Master incrementally. Want MasterRecord's migrations in an existing Express app? Take just that. Want the scaffold but plan to swap the ORM later? The seams are visible on purpose.

  • Each layer ships and versions on its own.
  • You can replace one piece without rewriting the others.
  • The conventions are shared, so the pieces still feel like one framework.

The part I care about most

The thing I care about is not that Master exists. Plenty of frameworks exist. What I care about is that the first few minutes of a project feel good again. Starting an app should not require hours of glue code before you can touch the product. The cost of a new idea should be low enough that you try the idea instead of dreading the setup.

Concretely, this is the loop I wanted, and the loop Master gives you:

A terminal window emitting an organized grid of folders and files, suggesting instant project scaffolding from a single command.
One workflow scaffolds the model, controller, route, migration, and frontend page together.
  1. 1Create the app with one command.
  2. 2Generate a resource (model, controller, route, and a frontend page together).
  3. 3Run the migration.
  4. 4Start building the part that is actually your product.
From zero to running

npm install -g master, then master new my-app, master db migrate, and master dev. That boots the API on port 3001 and the Next.js frontend on port 3000. The next thing you write is product code, not configuration.

Why not just use Next.js?

Next.js is genuinely good, and Master does not try to replace it. It puts Next.js where I think it belongs, as the frontend layer, and then gives you a real backend and ORM underneath. The result is a decoupled full-stack app where each layer does one job: Next.js renders the UI, MasterController handles the API, MasterRecord handles the database.

I reach for this split because a lot of apps eventually outgrow the idea that everything is just a frontend with some API routes bolted on. The day your business logic stops fitting neatly inside server actions and route handlers is the day you wish you had structure. Master gives you that structure on day one, before you need it, instead of forcing a painful refactor later.

Three connected layers on a dark background: a browser UI on top, an API server with routing arrows in the middle, and a database with relationships at the bottom.
Next.js frontend, MasterController API, and MasterRecord ORM: separate services, created together.
  • You need real backend structure, not handlers scattered across a pages tree.
  • You need database workflows: migrations, relationships, a schema you can reason about.
  • You need controllers and middleware that compose.
  • You need security primitives that ship by default.
  • You need a home for business logic that does not feel like a hack.

How a request actually flows

It helps to trace one request end to end, because the architecture is the whole pitch. A user clicks something in the Next.js app. The frontend calls the MasterController API over HTTP. MasterController runs the request through its middleware stack, hits a controller method, and that controller talks to the database through MasterRecord. The model returns plain data, the controller shapes the response, and Next.js renders it.

Nothing here is exotic. That is the point. It is the same separation that server frameworks have used for two decades, expressed in modern JavaScript and ESM, with the wiring already done. The frontend and backend are separate services running on separate ports, but they were scaffolded together and they share conventions, so they behave like one app instead of two projects you are forever syncing by hand.

The middleware stack is where most of the unglamorous-but-mandatory work lives, and Master ships it on by default rather than as an afternoon you schedule for later. CORS, CSRF protection, rate limiting, secure headers, and HSTS are configured the moment the app boots. WebSockets are part of MasterController too, so real-time features are a first-class path instead of a second server you stand up and babysit. The defaults are the kind of thing you only notice when a framework forgets them and you ship a security gap you did not know was your job.

Decoupled, not disconnected

Separate services give you room to scale the frontend and backend independently, deploy them on their own cadence, or put the API behind a different domain. Created together means you do not pay the usual integration tax to get there.

MasterRecord: the part I rebuilt most

If one layer earned the most rewrites before I was happy, it was the ORM. Data access is where most full-stack projects quietly rot. The model layer drifts from the database, migrations turn into a folder nobody trusts, and relationships become a thing you look up every single time. I wanted MasterRecord to make the schema feel like something you own rather than something you fight.

MasterRecord is code-first. You define models in code, generate migrations from changes, and run them through the CLI. Relationships, transformers, and the usual query surface are built in, and it speaks SQLite, MySQL, and PostgreSQL so your local setup and your production database do not have to be the same engine. That last part matters more than it sounds: developing on SQLite and deploying on MySQL or Postgres is a workflow Master treats as normal, not as a footgun.

Transformers are the piece I lean on more than I expected. They give you a defined boundary between how data sits in the database and how it leaves your API, which is exactly where a lot of apps leak internal shape into their public contract and regret it. And because migrations are generated and reversible, schema changes stop being the scary part of a deploy. You change a model, generate the migration, review it, run it, and roll it back if you were wrong. The schema becomes a thing under version control that you reason about, not a folder of files everyone is afraid to touch.

master generate model Order
master db migrate
master db rollback   # when you change your mind
  • Code-first models keep the schema next to the logic that uses it.
  • Migrations are generated and versioned, not hand-edited and hoped over.
  • Relationships and transformers are first-class, not bolted on.
  • SQLite, MySQL, and PostgreSQL are all supported out of the box.

What Rails got right

The title of this post is not nostalgia for Ruby. It is for a specific idea Rails made famous: convention over configuration. When the framework makes the boring decisions for you, with sensible defaults, you stop spending willpower on folder structure and naming and start spending it on the product. JavaScript spent a decade celebrating the opposite, the freedom to assemble everything yourself, and the bill for that freedom is decision fatigue.

Master borrows the conventions, not the language. If you have used Rails, the mental model maps almost one to one. If you have not, the conventions are simple enough that they read as obvious within an afternoon.

The under-appreciated benefit of strong conventions is what happens when someone else opens the project. A new contributor on a convention-driven app already knows where the models live, where routes are declared, and how a migration gets generated, because the framework decided those things, not the last person who touched the repo at midnight. Onboarding stops being an oral tradition. That is the quiet productivity Rails sold, and it is the part I most wanted back.

RailsMasterWhat it does
rails newmaster newScaffold a complete app in one command
Active RecordMasterRecordCode-first models, migrations, relationships
Action ControllerMasterControllerRouting, controllers, middleware
rails generatemaster generateGenerate a model, controller, route, and page
rails db:migratemaster db migrateApply schema changes
Convention over configurationSame philosophySensible defaults, far less boilerplate

Where Master fits in the Node.js landscape

Master is not the first attempt to bring full-stack structure to JavaScript, and I would be lying if I pretended the field was empty. NestJS gives you a structured backend. AdonisJS ships a backend plus an ORM. Redwood and Blitz tried to fuse the frontend and backend into one opinionated whole. Express is still the default everyone reaches for, and the data backs that up: the State of JavaScript 2025 survey, which collected responses from roughly 13,000 developers, found Express still leading backend usage while newer entrants like Hono and Nitro climb on satisfaction.

The same survey found that developers have used an average of only 2.6 front-end frameworks across their entire careers and tend to stick with about 1.7 meta-frameworks. People are not framework-hopping the way the memes suggest. They settle. That is the real argument for getting the conventions right: the choice is sticky, so the defaults you ship with matter for years.

Here is the honest map of where Master sits and what each approach leaves on your plate.

ApproachWhat you getWhat you assemble yourself
Next.js aloneUI, routing, API routes, server actionsORM, migrations, backend structure, jobs
Express or FastifyA minimal HTTP layerAlmost everything: ORM, structure, conventions, frontend
NestJSA structured, modular backendFrontend, ORM choice, migration wiring
AdonisJSFull backend plus the Lucid ORMA separate, less Next.js-native frontend story
MasterNext.js frontend, MasterController API, MasterRecord ORM, one commandLess: the layers are wired together from the start

Who Master is for

Master is for developers who like JavaScript but miss productive full-stack frameworks. If your instinct when you open a blank Node project is to wish it gave you more out of the box, you are the audience. If your instinct is to assemble everything yourself, exactly the way you like it, you probably are not, and that is fine.

  • People who want to build real apps, not spend day one choosing packages.
  • Small teams that want a clear, shared structure from the start.
  • Solo founders who need to move fast without making a mess they regret.
  • Backend developers who want Node.js to feel less scattered.
It is probably not for everyone

If you love building every piece from scratch, Master will feel too opinionated. If your whole app fits comfortably inside a serverless-only frontend flow, you may not need it. But if you have ever opened a blank Node.js project and thought, I wish this gave me more out of the box, then Master is aimed squarely at you.

What works today

Master is in v2. It supports modern ESM and Node 20+, and it is MIT licensed, so there is no rug to pull. This is not a roadmap of promises; it is what you can run right now.

  • Scaffold a full-stack monorepo from one command.
  • Generate models, controllers, pages, and routes together.
  • A backend framework with routing, middleware, WebSockets, CORS, and security defaults.
  • An ORM with code-first models, migrations, relationships, and SQLite, MySQL, and PostgreSQL support.
  • MIT licensed, ESM-native, Node 20+.

What we actually run on it

I would not ask you to trust a framework I do not use myself. Bookbag, our AI customer support platform for ecommerce, runs on this stack. The MasterRecord migrations that shape Bookbag's production database run on the server as part of every deploy, before the app restarts, exactly the way the framework intends. Agents, conversations, actions, and billing all sit on models defined in MasterRecord and served through MasterController.

That is the real test. A scaffold is easy to make look good in a demo. Carrying a product that takes live actions against Shopify orders, processes returns, and handles billing is a different bar, and the framework has held up to it. The migration discipline in particular earns its keep here: when a schema change ships as a reviewable, reversible step that runs before the app restarts, a bad deploy is a rollback, not an incident.

If you want to see what gets built on top of this foundation rather than the foundation itself, the product side of the house is the proof. Bookbag is an agent that reads a store's knowledge and live data, takes real actions, and hands off to a human with full context when it should. All of that sits on Master's models, controllers, and migrations. The framework is boring underneath, which is the highest compliment I can pay infrastructure.

What I'd love feedback on

I am posting this because I want technical feedback from people who have shipped real apps, on Rails, Laravel, Django, Phoenix, Next.js, Express, NestJS, Adonis, or ASP.NET. Praise is nice; a sharp objection is more useful. I am especially interested in a few questions.

  • Is the Rails-productivity-for-Node direction genuinely useful, or solving a problem people have already made peace with?
  • Does the separation between Next.js frontend, API backend, and ORM feel right, or too heavy?
  • What would make you trust a new framework enough to start a real project on it?
  • What should the first canonical example app be?
  • Which parts of full-stack JavaScript still feel too painful to you?
A real framework, not a toy

The JavaScript world does not need another framework unless it gives developers a meaningfully better way to build. That is the bar I am holding Master to. Not a tiny library, not a starter kit, but a full-stack framework for shipping real applications with Node.js. If you try it, tell me where it breaks.

Key takeaways

  • Master is a full-stack framework for Node.js: one command scaffolds a Next.js frontend, a MasterController API, and a MasterRecord ORM, wired together.
  • It borrows Rails' convention-over-configuration philosophy, not its language, so the first hour of a project is boring and the rest is productive.
  • The three pieces are usable independently: opinionated enough to move fast, modular enough to understand and replace any layer.
  • MasterRecord is code-first with generated migrations and supports SQLite, MySQL, and PostgreSQL, so dev and production can run different engines.
  • v2 ships ESM, Node 20+, security defaults, and is MIT licensed.
  • Bookbag runs on this stack in production, with MasterRecord migrations applied on every deploy.

Frequently Asked Questions

Turn support into your competitive edge

Join the ecommerce teams resolving more tickets, answering 24/7, and turning support into a revenue channel with Bookbag.