Databases with Supabase
Give your app a memory
Without a database, your app forgets everything when the page reloads. A database gives it a memory. Supabase is a popular choice for AI-built apps because it's free to start and easy to set up.
What is a database?
A database is like a set of spreadsheets that your app can read and write. Each table is one sheet. Each row is one item, like one recipe. Each column is one detail, like the recipe's title.
Supabase is a service that gives you a database and more, without running your own server:
- Postgres database — where the tables live
- Auth — sign up and log in (covered on the next page)
- Storage — a place for files like photos
- Row Level Security — rules for who can read or change each row
Create a Supabase project
- Go to
supabase.comand sign in (a free account is enough). - Click New project and give it the name of your app.
- Choose a strong database password and save it in your password manager.
- Pick a region close to you and wait a minute while it sets up.
Put the keys in .env.local
Your app needs an address and a key to talk to Supabase. Find them in the project settings, under API (the dashboard layout changes, so look for "API keys"). You need:
- The Project URL
- The anon (or "publishable") key
Create a file called .env.local in the top of your project folder using VS Code. Paste the values in yourself. In a Next.js app it might look like this:
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-hereVariables that start with NEXT_PUBLIC_ are visible in the browser. That's OK for the anon key, but only when Row Level Security is on for every table.
NEXT_PUBLIC_ variable or in browser code. It skips all security rules. Also confirm .env.local is listed in .gitignore before your next commit.Create tables with SQL
Tables are created with SQL, a language for talking to databases. You have two options:
- Table Editor — click to add tables and columns in the Supabase dashboard. Fine for one small table.
- SQL Editor — paste SQL that your coding agent wrote. Better for several tables, because the SQL is saved in your project.
For example, imagine a recipe app. Ask your agent in plan mode:
Write a SQL file at supabase/schema.sql that creates the tables from docs/PRD.md.
Turn on Row Level Security for every table.
Add a policy so anyone can read published recipes.
Explain each table in one sentence before writing the file.Here is roughly what one table looks like:
create table recipes (
id uuid primary key default gen_random_uuid(),
title text not null,
slug text unique not null,
ingredients text,
steps text,
image_url text,
is_published boolean not null default true,
created_at timestamptz default now()
);
alter table recipes enable row level security;
create policy "Anyone can read published recipes"
on recipes for select
using (is_published = true);Let's break this down:
create tablemakes a new table with these columnsenable row level securitylocks the table. Nobody gets in without a rule.is_publishedlets you hide a recipe instead of deleting itcreate policyadds a rule. This one lets anyone read published recipes.
To run it, open your Supabase project, click SQL Editor, paste the whole file, and click Run. Then open Table Editor to see your tables.
Understanding Row Level Security
Row Level Security (RLS) is like a bouncer at every table. For each row, it checks a rule before letting someone read or change it.
Supabase marks tables without RLS as "unrestricted". If you see that label on any of your tables, ask your agent to fix it.
Seed some starting data
Seeding means filling a table with starting data. If you built your pages with sample data, turn it into real rows:
Turn the sample data file into a SQL insert script at supabase/seed.sql.
Keep the same values.Run seed.sql in the SQL Editor and check the Table Editor. Then connect the app:
Install the Supabase JavaScript client and connect the app using the
Supabase URL and anon key in .env.local.
Change the pages to load data from the database instead of the sample file.
Then delete the sample file. Show a friendly message if loading fails.Restart npm run dev so it picks up the new .env.local. Test it: change a value in the Table Editor and refresh the page. The new value should appear.
Storing files with Storage
Supabase Storage is like a shared photo folder for your app. Files go into a bucket, which is a named folder.
- In Supabase, open Storage and click New bucket.
- Give it a clear name like
recipe-images. Make it public only if everyone should see the files. - Upload a photo and copy its public URL into the matching row, for example the
image_urlcolumn.
Common mistakes
- Forgetting to restart the dev server after editing
.env.local. Next.js only reads it at startup. - Turning RLS off "to make it work". Ask your agent to fix the policy instead.
- Pasting keys into the chat. Write them into
.env.localyourself and tell the agent the variable names. - Typos in variable names. The name must match exactly in
.env.localand in the code.
Practice
Try these exercises to lock in what you just learned.
What's next?
Your app now has a real memory. Next, let's add login and access, so people can create accounts and only see their own data.