Skip to main content

Local development

The same Postgres, on your own machine, in one command:

snoutdata start

It runs a container, applies your migrations, runs your seed, and prints a connection string. Nothing about it touches your account or costs anything, so it is where you develop and where tests run; the hosted project is where you deploy.

It needs Podman and nothing else. The image is public and is pulled on first use. You do not need psql installed: when the machine has none, the container's own is used instead, which is what makes this work on a default Windows install.

Start, stop, status

snoutdata start [--port 54322] [--dir migrations] [--no-migrations] [--out-of-order]
snoutdata stop
snoutdata status
$ snoutdata start

Wrote .snoutdata/local.json. It holds a password, so put .snoutdata/ in your .gitignore.

Database running on port 54322
Migrations 3 applied
Seed seed.sql ran

postgres://postgres:[email protected]:54322/postgres

The URI is the only thing on stdout, so DATABASE_URL=$(snoutdata start) is correct with no filtering.

stop stops the container and keeps the data: start brings it back as it was. status says whether it is running, on what port and how big it has got, and answers plainly when there is no local database for this folder rather than treating that as an error.

note

--port defaults to 54322, which is the port supabase start publishes on. If you run both in one folder, give one of them another port, or you will get a raw address-in-use error from the container runtime rather than a sentence about the clash.

Migrations and the seed

A start applies migrations/*.sql in name order, once each, and then seed.sql if there is one.

It uses the same planner snoutdata db push uses: the same ledger table, the same refusals for a file that changed after it ran or a file that has gone, the same --out-of-order override, and the same -- snoutdata:no-transaction escape for a statement that cannot be wrapped. There is deliberately no second planner, because a local database that disagreed with the hosted one about what "already applied" means would be worse than no local database at all.

--no-migrations skips both. --dir points somewhere other than migrations.

Two projects in one folder

.snoutdata/local.json sits beside the .snoutdata/project.json that snoutdata link writes, and they do not interfere. A folder can be linked to a hosted project and have a local database at the same time, which is the ordinary case.

local.json holds a password, so .snoutdata/ belongs in your .gitignore. It is a password for a database on loopback that nothing off your machine can reach, so this is tidiness rather than a vulnerability, but the CLI says so the first time it writes one.

TypeScript types from your schema

snoutdata gen types typescript --local > database.types.ts # the local database
snoutdata gen types typescript > database.types.ts # the hosted project
snoutdata gen types typescript --schema public,billing --out src/database.types.ts

The output goes to stdout and nowhere else unless you ask, because the caller is usually a build script or an agent. Progress goes to stderr, --out names a file, and --json wraps the same text in one JSON value.

It reads the Postgres catalogs rather than information_schema, which is why an enum column comes out as its enum and an array as an array, where the portable view says USER-DEFINED and ARRAY. It is one query and it takes no lock.

Against a hosted project it needs psql on the machine. With --local it does not: it borrows the container's, like everything else here.

Also read