One backend for web and Android: lessons from Lettzio
Lettzio runs as a Next.js site and a Flutter app on top of the same Supabase database. Sharing one backend saved us a lot of work, as long as security lived in the database.

Lettzio is a rental marketplace for homes, rooms, hostels and offices across Pakistan. People use it on the web at lettzio.com and in the Android app. Both talk to the same backend, so a listing posted from a phone appears on the website straight away.
Here is what made that work, and what we would tell anyone planning the same setup.
Put the rules in the database, not in the clients
With two clients, any rule that lives in client code has to be written twice and will eventually drift. So the rules live in Postgres.
Lettzio uses Supabase, and every table has row level security. "Only the owner can edit a listing" is a policy on the table, not an if in Next.js and another in Dart. Both apps can use the same public key safely, because the database decides what each signed in user may read or write.
Auth needs a redirect for every client
Email links and Google sign in redirect back to the app after login. The web app returns to an /auth/callback route, and the Android app uses its own deep link scheme. Every one of those URLs has to be on the allow list in Supabase Auth. When sign in "works on web but not on the phone", a missing redirect URL is the first thing to check.
Uploads go straight to storage
Listing photos are large, and routing them through our own server would be slow and costly. Instead the server signs a short lived upload URL, using the S3 request presigner, and the client uploads straight to object storage. The server only stores the final path. The same flow works from the browser and from Flutter.
Use a separate service for the heavy work
Most requests are simple reads and writes that go straight to Supabase. Heavier jobs, such as processing images, run in a small FastAPI service. Keeping that split clear means the apps stay fast, and the expensive work can scale on its own.
What we would keep
- One schema, with security in the database
- Thin clients that mostly render and submit
- Direct uploads to storage with signed URLs
What we would do earlier
- Write the row level security tests on day one, not after launch
- List every auth redirect URL before the first mobile build
Two clients on one backend sounds like double the work. Done this way, it is closer to one and a half.
