Keep your frontend. Mandala is the backend.
“Web framework” covers two different jobs. One draws pages. The other holds the data, the rules and the API behind them. Mandala has no wish to draw your pages. Its goal is to replace the second job entirely. It does a good part of that today, and this page says exactly which part.
Checked 12 October 2026
Next.js, Astro, or the view layer of whatever you use. Pages, design, URLs, search engines, the customer’s experience.
- Pages and layouts
- Design
- SEO
- Checkout flow
What you would otherwise write in Rails, Django, Laravel or Express, or rent from a backend service.
- Data model
- Rules and permissions
- API
- History and audit
- Search
- Background work
- Sign-in for your users · roadmap
- Email and PDF · built, not released
- SDKs · roadmap
Two kinds of framework.
The first two are where your pages live, and Mandala is meant to sit behind them. The other four are complete backends, proven over many years, and they are what Mandala sets out to replace.
React with routing, server rendering, static generation and caching built in. Very strong for public, designed, search-friendly sites. It brings no data layer, sign-in or jobs of its own, which is the gap a backend fills.
Content-first sites that ship little JavaScript, with islands of interactivity where needed. The storefront that runs on Mandala’s contracts today is an Astro site.
One framework for a whole product: data layer, jobs, cache, realtime updates, a sign-in generator, and deployment to your own server set up from the start.
Models, migrations, sign-in, permissions and an admin generated from your models. Long support windows and a careful release process.
A complete PHP framework: data layer, queues, policies, websockets, and first-party hosting and tools around it.
Routing and middleware for Node, and nothing you didn’t ask for. A thin, well-understood base for an API.
Hosted backends such as Supabase and Firebase are the other thing people use for this job. They have their own page: Mandala vs backend services.
You write the rule. You don’t write the backend.
In a backend framework, one business rule ends up in several places: a migration, a model validation, a controller, a permission policy, an API serializer, and an audit library you added later. Keeping them in step is the job.
In Mandala the contract on the right is the only place the rule lives. Storage, the check, the lifecycle, the API and the history all come from it. Your frontend calls the API; it never re-implements the rule.
REST with an OpenAPI description, GraphQL with queries and mutations, and gRPC, per contract. Nothing is exposed unless the contract says api public or api authenticated.
A command carries its caller’s own signature, so the history says who did it, not “the web server”.
None of these frameworks keeps an audit trail in its core; each has well-known libraries for it. Here there is nothing to add.
# Nothing ships to the Moon, or over 200 units,# without a second person confirming (Leela).action Ship() authorize dhl validate state.paid or state.credit_terms state.crates_checked == state.crates state.status == Requested or state.status == Confirmed state.status == Confirmed or state.units <= 200 state.status == Confirmed or state.destination != "Moon" apply state.status = Shipped
The whole contract (65 lines)
aggregate Delivery state public customer: Text(len=200) public destination: Text(len=100) public units: Int = 0 public crates: Int = 0 public crates_checked: Int = 0 public paid: Bool = false public credit_terms: Bool = false public requested_by: Identity public confirmed_by: Identity public status: Enum(New, Requested, Confirmed, Shipped) = New action Request(customer: Text(len=200), destination: Text(len=100), units: Int, crates: Int, credit_terms: Bool) authorize dhl validate state.status == New units > 0 crates > 0 apply state.customer = customer state.destination = destination state.units = units state.crates = crates state.credit_terms = credit_terms state.requested_by = context.signer state.status = Requested action CheckCrate() authorize dhl validate state.status == Requested state.crates_checked < state.crates apply state.crates_checked = state.crates_checked + 1 action Confirm() authorize dhl validate state.status == Requested context.signer != state.requested_by apply state.confirmed_by = context.signer state.status = Confirmed action Pay() authorize dhl validate state.status != New state.paid == false apply state.paid = true # Nothing ships to the Moon, or over 200 units,# without a second person confirming (Leela).action Ship() authorize dhl validate state.paid or state.credit_terms state.crates_checked == state.crates state.status == Requested or state.status == Confirmed state.status == Confirmed or state.units <= 200 state.status == Confirmed or state.destination != "Moon" apply state.status = Shipped
Real DHL, exactly as it runs: this contract compiles. Every action is checked against it before it happens, for people and AI alike.
| Your frontend sends | Does |
|---|---|
POST /v1/{tenant}/{namespace}/actions/{Action} | A signed command |
POST /v1/{tenant}/{namespace}/queries/{Query} | A declared query |
POST /v1/{tenant}/{namespace}/graphql | Either, as GraphQL |
A shop, with an Astro front and contracts behind.
This is not a plan. In the repository there is a storefront whose pages are Astro and whose whole backend is seven DHL contracts, served by the engine’s generated API on three voters. Astro owns the HTML, the layouts and the SEO. It holds no business rule.
The catalogue, a visitor’s cart, the quote, and checkout to a durable order with stock reserved from the right lots. Prices and totals are computed by the contracts, not by the page.
A guest’s browser makes a key, the store grants it the right to buy, and every cart action is signed by that visitor.
An online grocer’s existing Astro site now reads its pages, catalogue and guest basket from contracts in a preview. Its checkout, orders and customer accounts are not connected yet and still run on the system it had.
Plainly: these are previews on a private network, not shops taking money. Payments and delivery are not connected. The client the storefront uses is a small hand-written one, not an SDK.
Its test flow opens a cart, adds two lines, quotes them, checks out, and verifies a durable order with a total computed by DHL. It never inserts an order directly.
What a backend does, and where Mandala is.
Replacing the backend is the goal. This is the list of what a backend has to do, with where each part stands, read from the code and not from the plan.
| A backend must | Status | Exactly |
|---|---|---|
| Model and validate data | works today | Typed state, actions and validation in a contract, checked before every write. |
| Change the schema | works today | A new version names the one it upgrades from and how fields move. Records are upgraded one by one, each by a signed action; there is no bulk migration. |
| Enforce business rules | works today | The same check for every caller, with refusals that name the rule. |
| Decide who may do what | works today designed | Delegation with a scope and an end date works today. Roles as tags, and decisions that need several people, are designed. |
| Serve an API | works today | REST, GraphQL (queries and mutations) and gRPC, generated per contract and served by the engine, with bearer or session credentials. Inside the Workspace the same API sits behind the owner’s sign-in, so an outside application talks to the App server. |
| Keep history and audit | works today | Every accepted change is a signed fact. Refusals are recorded too. |
| Search | works today built, not released | Ranked text search from a query works today. Indexed text, vector, graph, time-series and geo search are built on a branch. |
| Run background work | works today on the roadmap | Mandala’s own background work is durable and survives restarts. Schedules and queues that a contract declares for itself are on the roadmap. |
| Send email, make PDFs | built, not released | A contract can declare an email or a PDF document as an effect of an action. The engine has the durable outbox, the retries and the signed receipts. It is not yet switched on in Mandala. |
| Call webhooks, send SMS, take payments | designed | Named as the next kinds of effect. None is built in the engine. |
| Use any connector from a rule | on the roadmap | Today connectors serve the accomplice and background jobs. No contract can call one yet. The aim is that any connector is usable from DHL. |
| Run workflows with human steps | works today in progress | Steps that wait for a person’s decision, review or approval work in Projects. A visual editor for them is in progress. |
| Push live updates | designed | Live queries, where a screen’s answer changes as the data does. Nothing is built; the API has no subscriptions. |
| Sign your users in | on the roadmap | OAuth, email and password, and sessions for an application’s own users are not built. Today: passkeys for the owner, and guest sessions in the storefront preview. |
| Give your frontend an SDK | on the roadmap | SDKs for Next.js and Astro, and typed clients generated from contracts, do not exist yet. Today you call the API yourself. |
| Store and deliver files | on the roadmap | There is no file type in a contract and no public file delivery. The storefront preview serves images from its own image service. |
| Face the public internet | on the roadmap | Today: public and authenticated operations, a connection cap, a body limit and deadlines. No per-caller rate limits, API keys or abuse protection. |
| Separate dev, staging and production | partly today on the roadmap | You can try a contract on a throwaway node, preview an action, and dry-run an effect. Nothing promotes contracts or data between environments. |
| Serve many tenants | works today not planned | The engine isolates tenants, each with its own keys, actors and contracts. In Mandala a tenant is a person or a business on your own node. Signing up tenants and billing them, as a hosted service would, is not planned. |
Said plainly.
A Rails or Django application signs users in, sends mail, takes payments, stores files and runs scheduled jobs today. Mandala does not, yet.
These frameworks carry very large public sites. Mandala’s API has run previews. Nobody has leaned on it from the open internet.
Payments, maps, single sign-on, search services: a framework has a maintained package for each. Mandala has a short list of connectors and an SDK to write more.
You can hire a Rails or Django developer this month. Nobody has DHL on their CV yet. The language is small and the accomplice writes it, but that is the truth today.
Published support windows, security teams, years of patch releases. Mandala is young and moving fast.
Nothing goes to the Moon until a second person confirms it. I’ve asked Leela.
state.status == Confirmed or state.destination != "Moon"
Choose honestly.
The pairing Mandala is built for is your frontend framework in front and Mandala behind it. Whether you can take that pairing today depends on what your application needs from the table above.
Choose a framework’s own backend if…
- You need to be in production this quarter with customer sign-in and payments.
- You need a mature ecosystem: a package for every service, and answers for every error.
- Your team knows Rails, Django or Laravel. That is worth a great deal.
- You expect heavy public traffic and need a stack that has carried it before.
Choose Mandala as the backend if…
- The hard part of your application is its rules: records, steps, approvals, who may do what.
- You need the history of every change, signed, without building it.
- You want an AI to work in the application safely, under the same rules as people.
- Your users are few and known today, such as a team’s internal tool, and you can wait for public sign-in.
In between, there is a transitional path that an online grocer is taking now: keep the existing backend for sign-in, payments and orders, and move one part at a time behind contracts, starting with the catalogue and the cart. For developers · Mandala vs backend services · Rules, not code
We checked every statement about other products on 12 October 2026, against their own sites, documentation and repositories where we could. Products change. If something here is wrong or out of date, write to [email protected] and we’ll correct it.