Giving a low-code app a real back end with Supabase
reading · 00%

Giving a low-code app a real back end with Supabase

Auth, storage, and database logic for a Retool or FlutterFlow app, without writing a custom API.

Published
Reading5 min
CategoryProduction
Words1,179

The screens came together quickly. Then the app needed sign-in, somewhere to keep uploaded files, and a rule that each sales rep sees only their own accounts. Each need pulled in another service: Auth0 for sign-in, S3 for files, a hosted database, and a custom API to stitch them together.

That stitching is backend integration: connecting the systems behind an app so they work as one. It covers authentication, data storage, business logic, and scheduled work. API integration links software interfaces, while backend integration covers what sits underneath them.

For a Retool or FlutterFlow app, Supabase puts most of that in one place. This piece covers how to set it up so the app holds up in production.

What a production back end has to provide

A low-code front end handles the screens and the simple logic. A production app also needs:

  • sign-in, sessions, and permissions by role

  • structured data with real queries, indexes, and relationships

  • file storage with secure upload and delivery

  • server-side logic for validation, calculations, and calls to other services

  • a record of who changed what

Without those, the app stays a prototype, however finished the screens look.

What Supabase provides

Supabase is a back end built on Postgres. One project gives you:

  • a Postgres database with full SQL

  • authentication with email and password, social sign-in such as Google and GitHub, magic links, and multi-factor authentication

  • storage buckets whose access rules follow the same users

  • Edge Functions, written in TypeScript and triggered over HTTP or on a schedule

  • REST and GraphQL APIs generated from the schema

  • real-time updates pushed to subscribed clients

  • row-level security, so access rules run inside the database

Because it is standard Postgres, the data stays portable. Supabase can also be self-hosted, which matters to teams with data residency rules.

Step 1: map the data and the access before the schema

Write down who uses the app, what each role can see, and what each role can change. Then design the tables around those answers. Access rules that match the business are much easier to write when the schema was built for them.

Step 2: put access rules in the database

Row-level security runs before any query returns. If the app has a bug, the database still refuses rows the user should not see. This is the right guard for FlutterFlow, and for any client that signs users in through Supabase Auth.

SQLaccounts_rls.sql
alter table accounts enable row level security;

-- Reps read and update only the accounts they own
create policy "reps read own accounts"
on accounts for select
to authenticated
using (owner_id = auth.uid());

create policy "reps update own accounts"
on accounts for update
to authenticated
using (owner_id = auth.uid())
with check (owner_id = auth.uid());

auth.uid() returns the signed-in user's id from their Supabase session. A request without a session matches no policy, so it sees nothing.

Step 3: connect Retool with its own role

Retool connects to Supabase's Postgres as a standard database resource, and its SQL editor queries the tables directly. That connection signs in as one database role, so policies keyed on auth.uid() do not see individual Retool users.

Retool covers that layer itself. Permission groups decide who can open each app and use each resource, sign-in goes through SSO, and Retool's audit logs record user actions. On the database side, create a dedicated role for Retool with only the grants it needs. Never connect it as the postgres superuser.

SQLretool_role.sql
create role retool_app login password 'from-your-secret-store';
grant select, update on accounts to retool_app;

-- Row-level security is on, so this role needs policies of its own.
-- Retool's permission groups decide which users reach these rows.
create policy "retool app reads accounts"
on accounts for select to retool_app using (true);

create policy "retool app updates accounts"
on accounts for update to retool_app using (true) with check (true);

Step 4: log every change where the data lives

A trigger records each insert, update, and delete, whichever app made it. Copy this, then attach it to each table that needs a history.

SQLaudit_trigger.sql
create table audit_log (
  id         bigserial primary key,
  table_name text        not null,
  record_id  text,
  action     text        not null,              -- INSERT | UPDATE | DELETE
  actor      uuid        default auth.uid(),    -- null for non-Supabase-Auth connections
  db_role    text        default current_user,  -- e.g. the Retool role
  before     jsonb,
  after      jsonb,
  created_at timestamptz not null default now()
);

-- No policies: the trigger writes, and nobody reads it through the public API
alter table audit_log enable row level security;

create or replace function log_change() returns trigger
language plpgsql security definer set search_path = public as $$
begin
  insert into audit_log (table_name, record_id, action, before, after)
  values (
    tg_table_name,
    coalesce(to_jsonb(new), to_jsonb(old)) ->> 'id',
    tg_op,
    case when tg_op in ('UPDATE', 'DELETE') then to_jsonb(old) end,
    case when tg_op in ('INSERT', 'UPDATE') then to_jsonb(new) end
  );
  return coalesce(new, old);
end;
$$;

create trigger accounts_audit
after insert or update or delete on accounts
for each row execute function log_change();

Pair the database record with Retool's audit log, and you can answer who changed what from either side.

Step 5: move server-side logic into Edge Functions

Some logic belongs in neither the screen nor the database. Edge Functions take it:

  • webhooks from services such as Stripe, Twilio, or Slack

  • validation rules too complex for a database constraint

  • calls to third-party APIs, so keys never reach the browser

  • scheduled jobs such as clean-ups, daily aggregates, and status checks

Treat each function like any other code: in source control, with error handling, logging, and a test before deploy. Supabase's CLI runs them locally first.

When this setup fits

It fits well when:

  • an internal tool needs a database, sign-in, and files, and nothing holds that data yet

  • a FlutterFlow app needs sign-in, storage, and access rules without a custom API

  • a team has no one dedicated to running back-end services

  • data residency rules call for self-hosting

It fits less well when the data already lives somewhere else. If customer records are kept in Salesforce or SAP, a copy in Supabase will drift from the original. Build the screen on the system where that data already lives. Supabase can still hold what is new, such as app-specific records and files. Integration awareness covers how to decide where each piece belongs.

It also fits less well for teams committed to a non-relational database, or for workloads that need custom sharding. Firebase is the common comparison: it stores documents rather than relational tables. Supabase suits data with relationships and queries you want to write in SQL.

Our view

For a Retool or FlutterFlow app that needs a back end and has no existing system to build on, we recommend Supabase. That is because one database, one auth system, and one place for logic are easier to secure and hand over than four stitched services. Put access rules and the audit trail in the database from the first table, because adding them later means reworking data people rely on.

The rest of what production asks for is in Cool demo. Now what?, and the security and compliance checklist is the version IT will ask for.

Worth passing along? Share this note.
LinkedInX
Newsletter

Get the next one in your inbox

We write when something is worth reading. Subscribing means you are happy to hear from us when that happens. Leave any time. Privacy policy.

Founder

John Miniadis

Keep readingProduction
Related notes

Still here? Good sign

More notes from real builds, written up for the next person who needs one.

All articles
ContactLet's chat
Let's chat

Tell us what
you're building

Describe the process that is slowing your team down: the inbox, the spreadsheet, the portal nobody owns. We'll come back with a concrete approach, a rough timeline, and, where it helps, a working prototype within weeks.

What happens next
  1. We read every messageAn architect reads what you sent before anyone replies.
  2. You hear back in ≤ 2 daysA short reply with first questions and a suggested next step.
  3. We scope and prototypeA scoping call, then a working surface, not a slide deck.
Where are you in the process?