Zero-Code AI App BuilderZero-Code AI App Builder
Vibe Coding

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

  1. Go to supabase.com and sign in (a free account is enough).
  2. Click New project and give it the name of your app.
  3. Choose a strong database password and save it in your password manager.
  4. 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:

.env.local
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here

Variables 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.

Never put the service_role (or "secret") key in a 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:

Prompt
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:

supabase/schema.sql
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 table makes a new table with these columns
  • enable row level security locks the table. Nobody gets in without a rule.
  • is_published lets you hide a recipe instead of deleting it
  • create policy adds 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.

Browser (anon key)
Supabase
Give me all recipes
→
←
Policy: anyone can read published ones → here they are
Delete a recipe
→
←
No policy allows it → denied
With RLS on, Supabase checks the rules before it returns any rows.

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:

Prompt
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:

Prompt
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.

  1. In Supabase, open Storage and click New bucket.
  2. Give it a clear name like recipe-images. Make it public only if everyone should see the files.
  3. Upload a photo and copy its public URL into the matching row, for example the image_url column.
If images don't show, ask your agent: "Images from Supabase Storage don't load. Check the Next.js image settings."

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.local yourself and tell the agent the variable names.
  • Typos in variable names. The name must match exactly in .env.local and in the code.

Practice

Try these exercises to lock in what you just learned.

Quiz
Not started
Which Supabase key must never appear in browser code or a NEXT_PUBLIC_ variable?
Quiz
Not started
Your new table shows as "unrestricted" in Supabase. What does that mean?
Quiz
Not started
You added your Supabase keys to .env.local, but the app still can't connect. What should you try first?

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.