Skip to contentNewLaunching soon on Product Hunt (opens in a new tab)

Keep the client. Change what operates it.

Backenly's data plane is PostgREST, so a supabase-js frontend keeps its query grammar. What you gain is the governed change path and the autonomy loop.

Who this is for
Teams on Supabase who want the operator, not a new client
What you already have
A supabase-js frontend and a Postgres schema
What you need
Governance without a frontend rewrite

The problem

Supabase is good infrastructure, which is exactly the issue when nobody on the team wants to own schema design, migrations, and RLS review. The assembly and the upkeep are yours. Switching platforms normally means rewriting every call site, which is why teams stay past the point where it is working for them.

What you would normally build

A rewrite of every data call, a new auth integration, a re-derivation of every security policy in a different dialect, and a migration window long enough to do all three.

What Backenly does

  1. Swap

    Change the import and the client construction

    The compatibility entry point is built on the v2 surface, which passes PostgREST's grammar through untouched. It emits PostgREST rather than translating into a narrower dialect, which is why it is complete rather than approximate.

  2. Keep

    Filters, embeds, upsert, and counts behave

    or(), select('*, author(*)') embeds, overlaps(), upsert with onConflict, and count: 'exact' via Content-Range. Every operation resolves { data, error } and never throws, which is the convention the frontend is written against.

  3. Move

    Bring the data and re-author the policies

    pg_dump in, then take a connection string and run your migrations, then reconcile with adopt_external_schema. Policies are re-authored in Backenly's claim form, where set_rls installs your predicate verbatim and reads pg_policies back before reporting success.

  4. Gain

    The part that was not on offer before

    A governed mutation kernel, behavioural verification after changes, and a loop that monitors and repairs without being asked.

The client swap
// before
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://xyz.supabase.co', SUPABASE_ANON_KEY)

// after
import { createClient } from '@backenly/sdk/supabase'
const supabase = createClient(
  'https://backenly.com/api/v1/<PROJECT_ID>',
  BACKENLY_ANON_KEY,
)

// unchanged: this is PostgREST on both sides
const { data, error } = await supabase
  .from('posts')
  .select('*, author(*)')
  .or('published.eq.true,pinned.eq.true')
  .order('createdAt', { ascending: false })

What you end up with

The same query grammar, the same { data, error } contract, and the same realtime subscription shape, over a backend where structural change is governed, verified, and reversible, and where a loop is watching between your deploys.

Who owns what

Backenly does

  • Serves PostgREST's grammar verbatim on the v2 surface, embeds included.
  • Maps auth and postgres_changes subscriptions onto Backenly equivalents.
  • Installs your RLS predicates verbatim and reads them back before reporting success.
  • Refuses what it cannot honour rather than approximating it.

You own

  • The data migration and the cutover window.
  • Re-authoring policies into the claim form, and testing them.
  • Replacing any rpc() call sites. There is no SQL-function surface.
  • Deciding whether the governance trade is worth it for your team.

What this is built on

The tools, routes and modules involved, by name, so you can go and check each one.

Capabilities used in Moving from Supabase
CapabilityWhat it does here
@backenly/sdk/supabaseCompatibility entry point emitting PostgREST directly, with { data, error } semantics and no throws.
/api/v2/{projectId}/{table}PostgREST grammar passed through untouched: filters, ordering, embeds, Prefer headers, Content-Range counts.
set_rlsPolicies as exact SQL, installed verbatim, read back from pg_policies. No model in the path.
pg_dumpFull schema export in either direction, so the move is reversible.
Autonomy loopContinuous monitoring and repair of the reversible safe band, the capability that has no Supabase equivalent.

Known limitations

Where this workflow stops. If one of these is load-bearing for you, it should decide it.

  • rpc() is refused. Backenly exposes no SQL functions by design, so any stored-procedure call sites need re-homing as event, cron, or HTTP functions.
  • Policies must be re-authored. They are not translated automatically from Supabase's dialect.
  • Storage and auth are Backenly's implementations, not Supabase's. The compat layer maps the common auth calls; anything provider-specific needs checking.
  • This is a client-compatibility bridge, not a one-click migration. Moving data and cutting over remain manual work.

Common questions

The common path does, because both sides are PostgREST clients and the mapping is largely the identity function. Filters, column projection, embedded resources, upsert with onConflict, and exact counts pass through. Test rpc() call sites and anything provider-specific before cutting over.

Try it on one free project

No credit card. Connect your agent over MCP and judge it by the verification evidence.