Engineering

Where a Xano backend stops being enough

A Xano backend rarely fails on the day you launch. It fails on the day two people click the same button in the same second. One request reads a row while another is halfway through writing it, and the same slot gets sold twice. The PostgreSQL database underneath is fully capable of stopping that. Whether it does comes down to a checkbox somebody had to know to tick, on every endpoint where it mattered.

The schema drifts before the traffic does

Xano's field types include Table Reference, which links a row to a row in another table the way a foreign key does. They also include Object and JSON, which let you park a bundle of values inside a single column. Both are documented, both work. The difference is that adding three more attributes to a JSON column takes about eight seconds, and giving them a proper table takes a schema change, a migration of existing rows, and a pass over every function stack that touched the old shape.

Guess which one wins on a Thursday afternoon. After a year you have order data living in three shapes: some in columns, some nested in an object, some duplicated into a second table because a screen needed it flat. Nobody was careless. The editor priced embedding cheaper than modelling, and everyone responded to the price.

You feel it later, when someone asks a question the shape cannot answer. A JSON column has no foreign key, so nothing stops a reference inside it from pointing at a row you deleted last March. Reporting turns into a function stack that loops over records and reassembles them in memory, which is slow in a way no index will fix.

Transactions cover less than most people assume

Xano ships a Database Transaction function, and it does what the name says. Its documentation is also unusually honest about the boundary: only database operations count when deciding whether to roll back. Other function types, including conditionals and data transformation, can fail inside the block without undoing the database work. So a stack that charges a card through an external API and then writes an order row is not atomic across both halves. The write can survive a failure that happened after it.

Locking is there too. The Get Record function has an option to lock the returned rows, and the docs are explicit that the lock has to sit inside a database transaction to hold other function stacks off the data. That is the right primitive. It is also opt-in, one function at a time, with nothing in the product that tells you which of your forty endpoints needed it.

That is the real problem with correctness as a checkbox. A missing lock is invisible during development, because you are one user. It stays invisible in staging, because your test traffic is sequential. It appears the first week real concurrency arrives, as a support ticket about a double booking that you cannot reproduce.

What you are actually renting

On Xano's pricing page today, direct database access starts on the Essential plan at $85 a month billed annually. Below that, the free tier caps you at 100,000 records with a rate limit of 10 requests per 20 seconds and no direct connection to the database. Pro runs $224 a month billed annually and comes with 25 GB of database SSD storage and a 14-day rolling backup. The data is yours, and Xano says so plainly in its FAQ. The ability to open a psql session against it is a line item.

When we moved Stay World Class off Webflow and Xano, the backend became NestJS on Supabase and the frontend became Next.js. Lighthouse went from 55.91 to 91 out of 100 and LCP came down 77%. The part that took the longest was not the API surface. It was a four stage ETL that carried the legacy integer IDs across into PostgreSQL UUIDs without breaking a single relationship, because years of records had accumulated shapes the original model never planned for.

What changed after was the class of bug we could catch. A transaction wraps whatever we put in it. A unique constraint refuses the second booking without anyone remembering to tick anything. The test suite runs on every push, in CI, against a schema in version control.

When staying on Xano is the right answer

If your write path is one person at a time, none of this reaches you. Internal tools, admin panels, client portals where two users editing the same record at the same instant is a hypothetical rather than a Tuesday. You will not hit the concurrency ceiling because you never approach it, and paying an engineering team to rebuild what already works would be a bad trade.

Staying is also right while the product is still moving. If the data model changes every fortnight because you are still finding out what the business is, a visual editor that absorbs a schema change in a minute is worth more than transactional rigour on a model you are about to throw away. Migrate when the shape has stopped arguing with you, not before.

The honest signal is not a feature you are missing. It is the second time you write a support ticket you cannot reproduce, and the first time somebody asks a question about your own data that the schema cannot answer.

Sources

Have a stack outgrowing itself?

Book a call and we'll walk you through how we'd approach your platform, priced fairly and estimated for real.