Open-source project
supabase-community/seed avatar
supabase-community/seed

Seed fills a Postgres schema with deterministic data you did not write

Automatically seed your database with production-like dummy data based on your schema for local development and testing.

787 stars37 forksTypeScriptMIT

At a glance

What is it?
Seed, maintained in the Supabase community organisation and built by Snaplet, reads your database schema and generates production-like rows for local development, with a typed TypeScript client and deterministic output. The AI text generation is an optional extra that needs an API key, and the project has not shipped a release since 2024.
Who is it for?
Seed suits a team that wants a populated local database minutes after cloning a repo, since it reads the schema instead of asking you to write fixtures, and the deterministic generation means the same seed produces the same data every run, which is what a test suite needs. It is the wrong tool if you need specific, hand written fixtures, since the point is generated values, and it is the wrong tool if your data has to match a production shape exactly.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem is empty tables after every clone

Seed is described in the README as a tool to automatically seed your database with production-like dummy data based on your schema, for local development and testing. The headline is blunt about the state it fixes: no data, no problem.

The problem is mundane and expensive. A new developer clones a repository, sets up Postgres, and faces a schema with no rows, so the application either crashes on its first query or shows empty states that hide real bugs. The usual answers are hand written fixture files that drift, or a copy of production data that nobody wants to put in a git repository.

Seed takes the middle path. It reads the database structure and works out the values, so the data exists without being written down, and it is deterministic, so the data is the same every time.

The project lives in the supabase-community organisation on GitHub, is MIT licensed, and the README carries the Seed by Snaplet branding, with the hosted documentation at snaplet-seed.netlify.app/seed and the same documentation also in the docs folder of the repository.

A generated TypeScript client is the interface

The type safety claim is the most distinctive part of the design, and it is best shown by the example the README gives:

ts
await seed.posts([{
  title: "Why you need Seed",
  author: {
    email: "[email protected]",
  },
  comments: (x) => x(3),
}]);

Seed creates a TypeScript client based on the database structure, so the seed.posts call and the shape of the object are derived from the schema rather than written by hand. The README describes the values as safe and softly documented, and says you have the full power of the TypeScript language and the rich Node.js infrastructure when seeding data.

Two features in that snippet carry the weight. The author is given as a nested object with an email, which is the automated relationships feature: Seed creates relational entities so you do not have to keep track of IDs in one table while defining values in another. The comments field is a function, (x) => x(3), which is how you request a count of related rows rather than a fixed set.

So the interface is not a config file format. It is ordinary TypeScript with generated types, which means your editor can autocomplete the table names and the field names, and a schema change shows up as a type error rather than a runtime failure.

Determinism comes from Copycat, not from luck

The deterministic data section names the mechanism: Seed uses Copycat for its data generation functions, and all data generation is fully deterministic, meaning that the same inputs always produce the same data outputs.

This is the property that makes the tool usable in a test suite rather than only in a demo. A test that asserts on generated content needs the same content every run, otherwise the assertion is either flaky or has to be loosened until it checks nothing. The README says exactly that, that this makes Seed good for consistent tests and development.

The reason this is not free is worth understanding. A generator that picks a random row from a country list or assigns a random number needs a source of randomness, and a deterministic generator needs that randomness to be seeded from the inputs rather than from the clock. Copycat, at github.com/snaplet/copycat, is a separate project by the same organisation that supplies those generation functions, and it is where that behaviour lives.

The built-in data is the other half of the value. The README says the default data automatically adds necessary built-ins such as country lists and currency codes, which is the part that stops a seeded user table from containing a country called nowhere.

One command to try it

The whole trial is one command:

bash
npx @snaplet/seed init

The init command is the only thing the README asks a user to run, and the package is published on npm under the @snaplet scope, so npx fetches it without a global install. The hosted documentation is linked right after, and the repository also carries a docs folder with the same content, so a reader can work from either.

The docs site is a separate project in the monorepo with its own package. The prerequisites are brew, git, pnpm and Node.js, and hosting it locally is two commands:

bash
cd docs
pnpm install

and then pnpm dev, which serves it at http://localhost:3000.

That the documentation has its own install path, separate from the tool itself, is a small signal about the shape of the project. The npm package is the product, and the docs are a Vite style site that happens to live in the same workspace.

LLM generated text is opt-in and writes a file you can edit

The AI generated data feature uses a Large Language Model to generate examples for text based entries. It needs one of two environment variables in your .env file:

code
OPENAI_API_KEY=<your_openai_api_key>
GROQ_API_KEY=<your_groq_api_key>

The model name can optionally be set with AI_MODEL_NAME, with gpt-4-mini given as the example.

What makes this feature more usable than a prompt is that the predictions are not thrown away after the call. The predicted data is saved in .snaplet/dataExamples.json and can be modified by the user, and the file has a shape that invites editing, with an input naming the column, an examples array, and a description of what that column is for.

The cycle is then: you edit a description or drop the examples, and npx @snaplet/seed sync regenerates examples based on the updated description for that column. So the model is doing the first pass on your text data and a human is correcting it, with the corrected version becoming the input for the next run.

The cost of this design is an API key in your local environment, and the risk is a generated example that looks plausible and is wrong, which is why keeping the file under version control and reviewing it matters more than it would with pure random data.

A pnpm and turbo monorepo with a Nix development environment

The repository layout shows a modern monorepo. The root package is @snaplet/root, private, with workspaces covering packages/* and docs, and pnpm 8.15.5 declared as the package manager with a required Node version of >=20.9.0.

The task runner is turbo. The root scripts delegate to it: build, clean, dev, test, test:e2e, type-check and type-check:watch all run turbo tasks, while lint runs eslint with --max-warnings 0 and then the turbo lint tasks. There is also a lint-monorepo script that uses knip to find unused files, dependencies and exports, including a --production --strict --include=unlisted pass that checks the published surface, and a visualize-deps script that uses skott to draw the dependency graph as a web app.

The local development environment is managed with Nix and direnv, which is more setup than a typical Node project asks for. The .envrc file at the root and the flake.nix and flake.lock files are the entry points, and the .github directory holds the workflows.

The result is a project where the tooling is as sophisticated as the tool, which is a fair trade for a code generator that has to keep up with schema changes, but it also means the contribution setup is not quick.

Where the project stands, and what to check first

The repository is not archived and the last push was on 2026-10-01. The release history is the thing to look at: v0.98.0 was tagged on 2024-07-30, with v0.97.20 and v0.97.19 in May 2024. So the published versions stopped in mid 2024 while the repository itself kept receiving commits, which usually means work continued on the main branch without new tags being cut.

That matters for a dependency, because there is no version on npm that necessarily contains the code you are reading in the repository. The package.json in the repository root is the workspace root, and the actual published packages live under packages/.

The licence is MIT, and the LICENSE file is at the root.

The concrete first step for an evaluator is small. Run npx @snaplet/seed init against a copy of a real schema, look at what it produces for the columns your code makes assumptions about, and check those assumptions. A generator will happily produce a price of zero, an empty string or a timestamp in the past, and the tables that break first are usually the ones with a foreign key and a unique constraint rather than the ones with a text column.

The second decision is whether to enable the LLM feature at all. It is genuinely useful for text heavy tables, and it introduces a network dependency and a key into a local setup, so for an offline or air gapped environment the deterministic path is the one to keep.

Editorial conclusion

Seed suits a team that wants a populated local database minutes after cloning a repo, since it reads the schema instead of asking you to write fixtures, and the deterministic generation means the same seed produces the same data every run, which is what a test suite needs. It is the wrong tool if you need specific, hand written fixtures, since the point is generated values, and it is the wrong tool if your data has to match a production shape exactly. Before adopting it, run npx @snaplet/seed init against a copy of your schema, check the generated values against what your code asserts on, and decide whether the LLM backed text generation is worth adding an API key dependency to your local setup.

Frequently asked questions

How do I try out Seed?

Run npx @snaplet/seed init, which fetches the @snaplet/seed package from npm and sets it up against your database. The hosted documentation is at snaplet-seed.netlify.app/seed, and the same documentation is in the docs folder of the repository.

Is Seed data deterministic?

Yes. Seed uses Copycat for its data generation functions and all data generation is fully deterministic, so the same inputs always produce the same outputs, which the README says makes it suitable for consistent tests and development.

How does the AI generated data feature work in Seed?

You set OPENAI_API_KEY or GROQ_API_KEY in your .env file, optionally with AI_MODEL_NAME. The predicted examples are saved to .snaplet/dataExamples.json where you can edit the descriptions or the examples, and running npx @snaplet/seed sync regenerates examples from the updated descriptions.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. supabase-community/seed on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/supabase-community-seed.svg)](https://hysenlabs.com/projects/supabase-community-seed)

Was this useful?