# How to Seed Data in Supabase

- Tool: Supabase
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Supabase (all plans), supabase CLI v1.0+
- Last updated: March 2026

## TL;DR

To seed data in Supabase, create a supabase/seed.sql file with INSERT statements and run supabase db reset to apply it. The seed file runs automatically after all migrations during a reset. Write idempotent seed scripts using INSERT ... ON CONFLICT DO NOTHING to avoid errors on re-runs. For larger datasets, use the COPY command or the Supabase JS client with bulk inserts. Seeding is essential for local development, testing, and demo environments.

## Seeding Data in Supabase for Development and Testing

Data seeding populates your database with initial or sample data for development, testing, and demo purposes. Supabase uses a seed.sql file that runs automatically after migrations during supabase db reset. This tutorial covers creating seed files, handling foreign key relationships, writing idempotent scripts, and seeding larger datasets.

## Before you start

- A Supabase project initialized with supabase init
- The Supabase CLI installed (brew install supabase/tap/supabase or npm install supabase)
- At least one migration file creating your tables
- Basic understanding of SQL INSERT statements

## Step-by-step guide

### 1. Create the seed.sql file

The Supabase CLI looks for a file at supabase/seed.sql in your project. Create this file and add your INSERT statements. The seed file runs after all migration files have been applied during supabase db reset. This means all your tables, indexes, and RLS policies are in place before the seed data is inserted. The seed runs with superuser privileges, so RLS policies do not block the inserts.

```
-- supabase/seed.sql
-- Seed data for local development

insert into public.categories (name, slug) values
  ('Technology', 'technology'),
  ('Design', 'design'),
  ('Business', 'business');
```

**Expected result:** A supabase/seed.sql file exists in your project with INSERT statements for sample data.

### 2. Run the seed with supabase db reset

The supabase db reset command drops the local database, re-applies all migrations in order, and then runs seed.sql. This gives you a clean database with known data every time. Use this during development when you want to start fresh. The reset command only affects the local Supabase instance started with supabase start — it does not touch your production database.

```
# Reset local database and apply all migrations + seed
supabase db reset

# Output:
# Resetting local database...
# Applying migration 20240101000000_create_tables.sql...
# Applying migration 20240102000000_add_indexes.sql...
# Running seed.sql...
# Finished supabase db reset.
```

**Expected result:** The local database is reset with all migrations applied and seed data inserted.

### 3. Write idempotent seed scripts

A seed script may be run multiple times (for example, when running supabase db seed without reset). To avoid duplicate key errors, use INSERT ... ON CONFLICT DO NOTHING or check for existing data before inserting. This makes your seed script safe to re-run without errors. For tables with auto-generated primary keys, you can also truncate the table before inserting.

```
-- Idempotent seed: use ON CONFLICT DO NOTHING
insert into public.categories (id, name, slug) values
  (1, 'Technology', 'technology'),
  (2, 'Design', 'design'),
  (3, 'Business', 'business')
on conflict (id) do nothing;

-- Alternative: truncate and re-insert
truncate public.posts cascade;
insert into public.posts (title, category_id, published) values
  ('Getting Started with Supabase', 1, true),
  ('UI Design Patterns', 2, true),
  ('Startup Funding Guide', 3, false);
```

**Expected result:** The seed script can be run multiple times without causing duplicate key errors.

### 4. Seed related tables with foreign key dependencies

When seeding tables with foreign key relationships, insert parent records before child records. If you use auto-generated IDs, you need to either hardcode IDs in the seed or use CTEs (Common Table Expressions) to capture the generated IDs and reference them in subsequent inserts. Hardcoding IDs is simpler for seeds but requires using the OVERRIDING SYSTEM VALUE clause for identity columns.

```
-- Seed with hardcoded IDs for related tables
insert into public.categories (id, name, slug)
overriding system value
values
  (1, 'Technology', 'technology'),
  (2, 'Design', 'design')
on conflict (id) do nothing;

insert into public.posts (title, category_id, published) values
  ('Getting Started with Supabase', 1, true),
  ('Advanced PostgreSQL Tips', 1, true),
  ('Design System Best Practices', 2, true);

-- Reset the sequence after hardcoded inserts
select setval(
  pg_get_serial_sequence('public.categories', 'id'),
  (select max(id) from public.categories)
);
```

**Expected result:** Parent and child records are seeded correctly with valid foreign key references.

### 5. Seed data programmatically with the JS client

For more complex seeding scenarios, you can write a TypeScript seed script that uses the Supabase JS client. This is useful when you need to generate random data, seed auth users (which requires the admin API), or apply business logic during seeding. Use the service role key to bypass RLS when seeding from a script.

```
// scripts/seed.ts
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'http://127.0.0.1:54321',  // Local Supabase URL
  'your-local-service-role-key' // From supabase status
)

async function seed() {
  // Seed categories
  const { data: categories } = await supabase
    .from('categories')
    .upsert([
      { name: 'Technology', slug: 'technology' },
      { name: 'Design', slug: 'design' },
    ], { onConflict: 'slug' })
    .select()

  console.log('Seeded categories:', categories?.length)

  // Seed posts referencing categories
  const techId = categories?.find(c => c.slug === 'technology')?.id
  if (techId) {
    const { data: posts } = await supabase
      .from('posts')
      .insert([
        { title: 'Supabase Tutorial', category_id: techId, published: true },
        { title: 'PostgreSQL Tips', category_id: techId, published: true },
      ])
      .select()

    console.log('Seeded posts:', posts?.length)
  }
}

seed().catch(console.error)
```

**Expected result:** Data is seeded programmatically using the Supabase JS client with service role access.

## Complete code example

File: `seed.sql`

```sql
-- supabase/seed.sql
-- Idempotent seed script for local development
-- Run with: supabase db reset (or supabase db seed)

-- ============================================
-- 1. Seed categories (parent table)
-- ============================================
insert into public.categories (id, name, slug, description)
overriding system value
values
  (1, 'Technology', 'technology', 'Software and engineering topics'),
  (2, 'Design', 'design', 'UI/UX and visual design'),
  (3, 'Business', 'business', 'Startups and entrepreneurship'),
  (4, 'Marketing', 'marketing', 'Growth and content marketing')
on conflict (id) do nothing;

-- Reset the sequence
select setval(
  pg_get_serial_sequence('public.categories', 'id'),
  (select coalesce(max(id), 0) from public.categories)
);

-- ============================================
-- 2. Seed posts (child table with FK to categories)
-- ============================================
insert into public.posts (title, slug, category_id, content, published)
values
  ('Getting Started with Supabase', 'getting-started-supabase', 1,
   'Learn how to set up your first Supabase project.', true),
  ('Advanced PostgreSQL Tips', 'advanced-postgresql-tips', 1,
   'Tips for optimizing your PostgreSQL queries.', true),
  ('Design System Best Practices', 'design-system-practices', 2,
   'How to build a scalable design system.', true),
  ('Startup Funding 101', 'startup-funding-101', 3,
   'A beginner guide to raising your first round.', false),
  ('SEO for SaaS', 'seo-for-saas', 4,
   'How to drive organic traffic to your SaaS product.', true)
on conflict (slug) do nothing;

-- ============================================
-- 3. Seed tags (many-to-many)
-- ============================================
insert into public.tags (id, name)
overriding system value
values
  (1, 'supabase'), (2, 'postgresql'), (3, 'react'),
  (4, 'typescript'), (5, 'tailwind')
on conflict (id) do nothing;

select setval(
  pg_get_serial_sequence('public.tags', 'id'),
  (select coalesce(max(id), 0) from public.tags)
);
```

## Common mistakes

- **Running supabase db reset against the production database** — undefined Fix: supabase db reset only affects the local database. However, always double-check which database you are connected to. Never run destructive commands against your production project.
- **Inserting hardcoded IDs without resetting the sequence afterward** — undefined Fix: After inserting rows with hardcoded IDs into identity columns, reset the sequence with setval(). Otherwise, the next auto-generated ID may collide with an existing seed ID.
- **Seeding child rows before parent rows, causing foreign key violations** — undefined Fix: Always insert parent table records first, then child table records. In seed.sql, order your INSERT statements so parent tables come before tables that reference them.
- **Including real user data or production secrets in the seed file** — undefined Fix: The seed file is committed to version control. Use only fake sample data for development. Never include real email addresses, API keys, or production user data.

## Best practices

- Use ON CONFLICT DO NOTHING to make seed scripts idempotent and safe to re-run
- Order INSERT statements so parent table records are created before child table records
- Reset auto-increment sequences after inserting hardcoded IDs with OVERRIDING SYSTEM VALUE
- Keep seed data realistic but fake — use descriptive sample content that helps with development
- Commit supabase/seed.sql to version control so all team members have the same development data
- Use TRUNCATE ... CASCADE before bulk re-inserts when you want to start fresh each time
- Separate seed data by environment — use seed.sql for local dev and a different script for staging

## Frequently asked questions

### Where should the seed.sql file be located?

The seed file must be at supabase/seed.sql in your project root. The Supabase CLI automatically looks for this file when running supabase db reset or supabase db seed.

### Does supabase db seed run with RLS enabled?

No, the seed.sql file runs with superuser privileges, bypassing all RLS policies. This means your INSERT statements will succeed regardless of any RLS policies on the tables.

### Can I have multiple seed files?

The CLI only looks for supabase/seed.sql. If you need multiple seed files, use SQL includes or organize your seeds in the single file with comments. Alternatively, write programmatic seed scripts that you run manually.

### How do I seed auth.users for testing?

You cannot directly INSERT into auth.users from seed.sql. Instead, write a programmatic seed script using the admin API: supabase.auth.admin.createUser({ email, password, email_confirm: true }). Use the local service role key.

### Does seeding work on the production database?

supabase db reset and supabase db seed only run against the local database started with supabase start. To seed production data, use supabase db push for migrations and a separate script for data. Be extremely careful with production data operations.

### What is the difference between supabase db reset and supabase db seed?

supabase db reset drops the entire local database, re-applies all migrations, and then runs seed.sql. supabase db seed only runs the seed.sql file on the existing database without dropping or re-migrating. Use reset for a clean start and seed to add data to an existing schema.

### Can RapidDev help set up a development workflow with Supabase seeding and migrations?

Yes, RapidDev can configure your local development environment with Supabase CLI, create migration files, write comprehensive seed data, and establish a workflow for your team to develop against consistent sample data.

---

Source: https://www.rapidevelopers.com/supabase-tutorial/how-to-seed-data-in-supabase
© RapidDev — https://www.rapidevelopers.com/supabase-tutorial/how-to-seed-data-in-supabase
