Automatically dump your database schema to version control after each migration
Geni can automatically generate a schema.sql file after running migrations, giving you a complete snapshot of your database structure that can be committed to version control. This makes it easy to see your current schema without reading through individual migration files.
When enabled, Geni dumps the complete database schema after each successful migration operation (geni up or geni down). The schema file includes all tables, indexes, constraints, views, and other database objects.From the migration code (src/lib/migrate.rs:66-70):
if dump_schema { if let Err(err) = database.dump_database_schema().await { log::error!("Skipping dumping database schema: {:?}", err); }}
Schema dumping is non-blocking - if it fails, your migration still succeeds. The error is logged but doesn’t affect migration status.
PostgreSQL schemas are generated using pure SQL queries - no external dependencies required.From src/lib/database_drivers/postgres.rs:187-445, the schema includes:Extensions:
-- EXTENSIONSCREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA public;
Tables:
-- TABLESCREATE TABLE users ( id integer NOT NULL, name character varying NOT NULL, email character varying NOT NULL, created_at timestamp without time zone);
Views:
-- VIEWSCREATE VIEW active_users ASSELECT * FROM users WHERE status = 'active';
SQLite schemas are extracted using the sqlite_master table.From src/lib/database_drivers/sqlite.rs:160-207:
let mut result = self .db .query("SELECT sql FROM sqlite_master", params![]) .await?;let mut schemas: Vec<String> = vec![];while let Some(row) = result.next().await.unwrap_or(None) { if let Ok(r) = row.get_str(0) { let text = r .to_string() .trim_start_matches('"') .trim_end_matches('"') .to_string() .replace("\\n", "\n"); schemas.push(format!("{};" text)); }}
Example SQLite schema.sql:
---- Sqlite SQL Schema dump automatic generated by geni--CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL);CREATE INDEX idx_users_email ON users(email);CREATE TABLE posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id));
MySQL schema dumping requires the mysqldump binary to be installed.
MySQL Dependency: MySQL schema dumping requires mysqldump to be installed on your system. This is pre-installed in the official Geni Docker image but may need to be installed separately in other environments.
# Check if mysqldump is availablewhich mysqldump# Install on Ubuntu/Debiansudo apt-get install mysql-client# Install on macOSbrew install mysql-client
MariaDB schema dumping requires the mariadb-dump binary.
MariaDB Dependency: MariaDB schema dumping requires mariadb-dump to be installed. This is pre-installed in the official Geni Docker image but may need to be installed separately in other environments.
# Check if mariadb-dump is available which mariadb-dump# Install on Ubuntu/Debiansudo apt-get install mariadb-client# Install on macOSbrew install mariadb
Dumping needs another binaries to work:- Postgres: Works without need for another binary. Uses SQL code to get schema- MySQL: `mysqldump` need to be installed(already installed in docker)- MariaDB: `mariadb-dump` need to be installed(already installed in docker)- Sqlite: Works without need for another binary. Uses SQL code to get schema- LibSQL: Works without need for another binary. Uses SQL code to get schema
When you commit, you’ll see both the migration and the schema change:
# New migration files+ migrations/1709123456_add_user_preferences.up.sql+ migrations/1709123456_add_user_preferences.down.sql# Updated schemamigrations/schema.sql:+ CREATE TABLE user_preferences (+ id SERIAL PRIMARY KEY,+ user_id INTEGER NOT NULL REFERENCES users(id),+ theme VARCHAR(20) DEFAULT 'light',+ notifications_enabled BOOLEAN DEFAULT true+ );+ + CREATE INDEX idx_user_preferences_user_id ON user_preferences(user_id);
Reviewing the schema.sql diff in pull requests makes it easy to understand the cumulative effect of migrations without reading through individual migration files.
docker run --rm -it \ -v "$(pwd)/migrations:/migrations" \ -e DATABASE_URL="sqlite://./db.sqlite" \ -e DATABASE_NO_DUMP_SCHEMA=true \ ghcr.io/emilpriver/geni:latest-slim up
From the README: “This image won’t try to dump the database” - use the slim image when you don’t need schema dumping or are using SQLite/LibSQL/PostgreSQL (which don’t require external tools).
The schema file is a complete, executable SQL file that recreates your database structure:
---- Postgres SQL Schema dump automatic generated by geni---- EXTENSIONSCREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;-- TABLESCREATE TABLE users ( id integer NOT NULL, name character varying NOT NULL, email character varying NOT NULL, created_at timestamp without time zone);CREATE TABLE posts ( id integer NOT NULL, user_id integer NOT NULL, title character varying NOT NULL, content text);-- CONSTRAINTSALTER TABLE users ADD CONSTRAINT users_pkey PRIMARY KEY (id);ALTER TABLE posts ADD CONSTRAINT posts_pkey PRIMARY KEY (id);ALTER TABLE posts ADD CONSTRAINT posts_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);-- INDEXESCREATE INDEX idx_posts_user_id ON public.posts USING btree (user_id);CREATE UNIQUE INDEX users_email_key ON public.users USING btree (email);
Quickly recreate the database structure in a new environment:
# Instead of running all migrationspsql myapp < migrations/schema.sql# Then mark all migrations as applied# (This is an advanced use case - normally you'd use geni up)
Don’t Use schema.sql as a Replacement for MigrationsThe schema file is for reference and review. Always use geni up to apply migrations in production. The schema file:
# Verify the environment variable isn't setecho $DATABASE_NO_DUMP_SCHEMA # Should be empty or "false"# Run migration with schema dumpunset DATABASE_NO_DUMP_SCHEMAgeni up