supabase-hello-world
"Run your first Supabase query \u2014 insert a row and read it back.\n\
Allowed Tools
Provided by Plugin
supabase-pack
Claude Code skill pack for Supabase (30 skills)
Installation
This skill is included in the supabase-pack plugin:
/plugin install supabase-pack@claude-code-plugins-plus
Click to copy
Instructions
Supabase Hello World — First Query
Overview
Execute your first real Supabase query: create a todos table in the dashboard, insert a row with the JS client, and read it back. This validates that your project URL, anon key, and Row Level Security are configured correctly before you build anything else.
Prerequisites
- Completed
supabase-install-authsetup (project URL + anon key in.env) @supabase/supabase-jsv2+ installed (npm install @supabase/supabase-js)- A Supabase project at supabase.com/dashboard
Instructions
The workflow is three steps: create the table (dashboard SQL), insert a row
with the JS client, and read it back to prove the round-trip. The skeleton
below is enough to follow along; the fully annotated code for every step —
including the error-branch comments and expected console output — lives in
the full implementation walkthrough.
Step 1: Create the todos Table
In the Supabase dashboard SQL Editor, create the table, enable Row Level
Security (required for anon-key access), and add permissive read + insert
policies for the hello-world exercise:
create table public.todos (
id bigint generated always as identity primary key,
task text not null,
is_complete boolean default false,
inserted_at timestamptz default now()
);
alter table public.todos enable row level security;
create policy "Allow public read" on public.todos
for select using (true);
create policy "Allow public insert" on public.todos
for insert with check (true);
Lock these policies down before production. Verify the table appears under
Table Editor before continuing.
Step 2: Insert a Row
Create the client from your env vars, then insert. Chain .select() to get
the inserted row back — .insert() alone returns { data: null }:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
)
const { data, error } = await supabase
.from('todos')
.insert({ task: 'Hello from Supabase!' })
.select()
Step 3: Read It Back
Select all rows and confirm the row you inserted is present:
const { data: todos } = await supabase.from('todos').select('*')
// [{ id: 1, task: "Hello from Supabase!", is_complete: false, ... }]
Open the Table Editor in the dashboard to visually confirm the row is
there. See implementation.md for the fully
error-handled version of each step.
Output
todostable created with RLS enabled- One row inserted via the JS client
- Same row read back with
.select('*') - Dashboard confirms the data round-trip
Error Handling
| Error | Cause | Solution |
|---|---|---|
relation "public.todos" does not exist |
Table not created | Run the Step 1 SQL in the dashboard SQL Editor |
new row violates row-level security policy |
RLS blocks the insert | Add the permissive insert policy from Step 1 |
Invalid API key |
Wrong anon key in .env |
Copy from Settings > API in the dashboard |
FetchError: request to https://... failed |
Wrong project URL | Verify SUPABASE_URL matches dashboard URL |
data is null after insert |
Missing .select() chain |
Add .select() after .insert() |
| Empty array returned from select | RLS blocks reads | Add the select policy from Step 1 |
Examples
Complete, runnable end-to-end scripts live in
references/examples.md — a full TypeScript script
(insert with .single(), then an ordered/limited read-back) and the Python
equivalent. Minimal TypeScript shape:
const { data } = await supabase
.from('todos')
.insert({ task: 'Hello!' })
.select()
.single()
- TypeScript — full script
- Python — full script (
pip install supabase)
Resources
Next Steps
Once the round-trip works, proceed to supabase-local-dev-loop for the local
development workflow with the Supabase CLI — running Postgres locally, applying
migrations, and syncing schema to your hosted project. Before shipping,
replace the permissive using (true) / with check (true) policies from
Step 1 with real per-user Row Level Security rules, since the hello-world
policies expose the table to anyone holding the anon key.