codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
Notes›data

Supabase

Published 2026-04-28· Updated 2026-05-18·0 views

Supabase

Supabase emerged as an alternative to Firebase, an open-source backend bundle. PostgreSQL sits at the core, with auth, storage, realtime, and serverless functions stacked around it.

1. About Supabase

Supabase started in 2020. The founders (Paul Copplestone and Ant Wilson) have stated the direction "put a Firebase-like developer experience on top of PostgreSQL." The code is published under Apache 2.0, and both managed cloud and self-hosting are supported.

Event Year
Project starts 2020
GA 2021
Storage added 2021
Edge Functions (Deno) 2022
pgvector default support 2023
Branching, Realtime improvements 2023 ~ 2024

2. Database (PostgreSQL)

The core itself is PostgreSQL. Users can connect with regular PostgreSQL clients (psql, JDBC, ORM). Extensions are richly enabled, and pgvector, pg_cron, postgis and similar extensions can be activated from the console.

Beyond public, system schemas like auth, storage, and realtime live in the same DB. As a result, RLS policies can read these system tables alongside user tables.

3. Auth (GoTrue)

GoTrue is an open-source auth server originally started by Netlify. Supabase forked and continues developing it. It supports email/password, magic link, OTP, OAuth, and phone authentication. Issued JWTs carry the user ID (sub) of auth.users, and RLS policies use that information.

4. Storage, Edge Functions, Realtime

Storage — implements an S3-compatible object store. The backend can also bind to other object stores via the S3 API. Metadata is stored in PostgreSQL's storage.objects table, with permissions handled by RLS. The practical side of Storage — buckets, uploads, and file permissions — is covered in the supabase-storage note.

Edge Functions — serverless functions running on the Deno runtime. Deploying TypeScript code lets it respond at the global edge. Database connections reach the same project's PostgreSQL.

Realtime — subscribes to PostgreSQL's logical replication and streams change events over WebSocket. Clients receive changes on specific tables and filters in real time. Broadcast and presence channels are also offered separately.

5. supabase-js client

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(URL, ANON_KEY);

// read
const { data, error } = await supabase
  .from('posts')
  .select('id, title, author:users(name)')
  .eq('published', true)
  .order('created_at', { ascending: false })
  .limit(10);

// write
await supabase.from('posts').insert({ title: 'hi' });

// auth
await supabase.auth.signInWithPassword({ email, password });

// realtime
supabase.channel('posts')
  .on('postgres_changes', { event: '*', schema: 'public', table: 'posts' }, (p) => console.log(p))
  .subscribe();

Characteristics.

  • Queries are translated by PostgREST (RESTful relational SQL mapping).
  • There are anon keys and service_role keys. The latter must be used only on servers.
  • Responses come as { data, error }, so handle with branching, not throws.

6. RLS — Row Level Security

PostgreSQL has provided row-level security since 9.5 (2016). Supabase stacks its own model on top.

ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

CREATE POLICY "own posts read" ON posts
  FOR SELECT
  USING (author_id = auth.uid());

CREATE POLICY "insert own" ON posts
  FOR INSERT
  WITH CHECK (author_id = auth.uid());
  • The auth.uid() function returns the user ID from the current JWT.
  • Policies split into USING (matching for read and UPDATE) and WITH CHECK (post-shape for INSERT and UPDATE).
  • The service_role key bypasses RLS — backend only.

The advantage is that permission rules live inside the DB. The same rules apply regardless of which client connects. The limit is that as policies grow, debugging gets harder, and complex joins can raise policy evaluation cost.

7. Self-hosting

All Supabase components are published as containers deployable via docker compose. The following containers come up together on one host.

db (Postgres), gotrue, storage-api, realtime
kong (gateway), studio (UI), meta, imgproxy

Trade-offs of self-hosting:

  • Pros: data sovereignty, cost control, integration with private networks.
  • Limits: backups, scaling, and upgrades become the operator's responsibility.

The choice between managed and self-hosted depends on data volume, sensitivity, and operational headcount.

8. Comparison with Firebase, Appwrite, PocketBase

Item Supabase Firebase Appwrite PocketBase
Core DB PostgreSQL Firestore (NoSQL), RTDB MariaDB SQLite (single binary)
License Apache 2.0 proprietary BSD-3 MIT
Self-hosting possible not available possible possible (single executable)
Auth GoTrue Firebase Auth own own
Realtime logical replication own (Firestore listeners) own own
Functions Deno Cloud Functions (Node) Functions (multi-language) (via Go middleware)
SDK js, py, dart, swift multi-language, multi-platform js, flutter, py, etc. js, dart

Firebase's strength is the maturity of mobile SDKs and Google Cloud integration. Supabase's strength is SQL, the relational model, and data portability. PocketBase comes up often for small projects thanks to its very small operational surface (single binary, SQLite). Appwrite offers a Firebase-like API model in open source.

9. Key separation and migration

The anon key is for public clients. RLS sets the permission rules. The service_role key is server-only and bypasses RLS.

Environment variable separation and CI checks are recommended so that service_role does not end up in built client bundles.

The supabase CLI handles SQL migration files and a local development environment (docker compose based). Commands like supabase db push and supabase db diff apply and inspect schema changes.

10. Common pitfalls

Tables without RLS — when a newly created table has RLS off, the anon key can read every row. The default being disabled is often pointed out.

service_role key exposure — once it lands in a client bundle, all data access becomes possible.

Function call cost in policies — calling heavy functions other than auth.uid() inside a policy evaluates them per row.

Free tier pause — managed free projects may pause after a period of inactivity (plans and pricing vary by period).

Storage RLS and metadata — object permissions follow policies on the metadata table. Missing policies cause unintended access.

Mixing migration tools — schema changes from Prisma or TypeORM mixed with the supabase CLI on the same DB scatter tracking. Pick one as SSOT.

Closing thoughts

Supabase is a good tool for small teams to do a lot quickly. As ops scale grows, the operational burden of self-hosting becomes visible, and managed costs become visible. That is when the choice between going with PostgreSQL alone or keeping the Supabase full stack arrives.

Next

  • supabase-storage
  • fcm-push
  • image-pipeline

References: Supabase official docs, Supabase architecture, Postgres RLS docs, PostgREST docs, Supabase self-hosting, PocketBase official.

More in data

All in this category →
  • Keep DB seed sources outside the code tree
  • Supabase Storage — File Upload and Permissions
  • Kafka in Practice — Topic Design and Message Flow
  • Orchestrating multiple PostgreSQL pools
  • Backup and Restore
  • Image Pipeline