Getting started with Supabase
Set up Supabase as your backend: postgres database, auth, and row-level security, with practical examples for a modern web app.

Supabase is an open-source backend-as-a-service built on top of PostgreSQL. It gives you a production-grade database, authentication, instant REST and realtime APIs, storage, and edge functions without managing infrastructure. In this guide I walk through setting up a project and using the core features that most apps need on day one.
What you get out of the box
- **PostgreSQL database** — a real relational database, not a NoSQL toy.
- **Auto-generated APIs** — REST (PostgREST) and GraphQL from your schema.
- **Auth** — email/password, magic links, OAuth providers.
- **Row Level Security (RLS)** — authorization enforced in the database.
- **Storage** — files with access policies.
- **Realtime** — subscribe to inserts, updates, and deletes.
1. Create a project and connect
After creating a project in the dashboard, you get a URL and an anonymous key. Connect a client like this:
ts
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
Never expose the service role key in the browser — it bypasses RLS and can read everything.
2. Reading and writing data
Tables are exposed automatically as endpoints:
ts
// Fetch profiles
const { data, error } = await supabase
.from("profiles")
.select("id, username, avatar_url")
.eq("username", "mark");
// Insert a row
const { error } = await supabase
.from("posts")
.insert({ title: "Hello Supabase", author_id: user.id });
3. Authentication
ts
// Sign up
await supabase.auth.signUp({ email, password });
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({ email, password });
// Get the current user
const { data: { user } } = await supabase.auth.getUser();
Supabase handles session storage, refresh tokens, and OAuth redirects for you.
4. Row Level Security (the important part)
By default, tables are locked down. You must write policies that decide who can do what. This is where most Supabase projects get security right or wrong.
sql
-- Allow users to read any public profile
create policy "Profiles are viewable by everyone"
on profiles for select
using ( true );
-- Allow a user to update only their own profile
create policy "Users can update their own profile"
on profiles for update
using ( auth.uid() = id );
A good rule of thumb: write policies as if the client is hostile. Assume anyone can send any request, and let RLS be the gatekeeper.
5. Realtime subscriptions
ts
const channel = supabase
.channel("room:lobby")
.on("postgres_changes",
{ event: "INSERT", schema: "public", table: "messages" },
(payload) => console.log(payload.new)
)
.subscribe();
When to use Supabase
- You want a relational database without running Postgres yourself.
- You need auth + storage + API quickly.
- You care about enforcing security close to the data.
Supabase shines for MVPs and mid-size products. For very custom infrastructure needs you may still drop to raw Postgres, but for most apps it removes a huge amount of boilerplate.