https://github.com/t1mmen/srtd
๐ช Supabase migrations made magical: Live-reloading SQL + Sane, reviewable diffs + Maintainable templates = Delightful DX
https://github.com/t1mmen/srtd
cli hot-reload live-reload migrations postgres repeatable-migrations sql-templates supabase supabase-cli supabase-db templates
Last synced: 13 days ago
JSON representation
๐ช Supabase migrations made magical: Live-reloading SQL + Sane, reviewable diffs + Maintainable templates = Delightful DX
- Host: GitHub
- URL: https://github.com/t1mmen/srtd
- Owner: t1mmen
- License: mit
- Created: 2024-12-16T20:18:08.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2025-03-31T18:50:19.000Z (22 days ago)
- Last Synced: 2025-04-09T19:16:49.183Z (13 days ago)
- Topics: cli, hot-reload, live-reload, migrations, postgres, repeatable-migrations, sql-templates, supabase, supabase-cli, supabase-db, templates
- Language: TypeScript
- Homepage: https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase
- Size: 1.84 MB
- Stars: 66
- Watchers: 1
- Forks: 4
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# `srtd` ๐ช Supabase Repeatable Template Definitions
> Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! ๐
[](https://www.npmjs.com/package/@t1mmen/srtd)
[](https://www.npmjs.com/package/@t1mmen/srtd)
[](https://opensource.org/licenses/MIT)
[](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
[](https://codecov.io/gh/t1mmen/srtd)[](./readme-demo.gif)
`srtd` enhances the [Supabase](https://supabase.com) DX by adding **live-reloading SQL** from templates into local db.
**Templates act as single-source-of-truth of your database objects**, that `build` to regular SQL migrations; This makes for sane code reviews and functional change history, (e.g `git blame` works as expected).
๐ Blog: [Introducing `srtd`: Live-Reloading SQL Templates for Supabase](https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase)
### Why This Exists ๐ค
While building [Timely](https://www.timely.com)'s next-generation [Memory Engine](https://www.timely.com/memory-app) on [Supabase](https://supabase.com), we found ourselves facing two major annoyances:
1. Code reviews were painful - function changes showed up as complete rewrites, `git blame` was useless
2. Designing and iterating on database changes locally was full of friction, no matter which workflow we triedI spent [nearly two](https://news.ycombinator.com/item?id=37755076) [years looking](https://news.ycombinator.com/item?id=36007640) for something pre-existing, to no avail. Sufficiently fed up, I paired with [Claude](https://claude.ai) to eliminate these annoyances.
Say hello to `srtd`.
[](./readme-screenshot.png)
## Key Features โจ
- **Live Reload**: Changes to your SQL templates instantly update your local database
- **Templates as source of truth**: Templates are the source of (non-mutable) database objects
- **Just SQL**: Templates as just SQL, and `build` to standard [Supabase](https://supabase.com) migrations when you're ready to ship
- **Sane code reviews**: Templates evolve like regular code, with diffs in PR's working `git blame`.
- **Developer Friendly**: Interactive CLI with visual feedback for all operations.Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
## Quick Start ๐
### Requirements
- Node.js v20.18.1 or higher
- [Supabase](https://supabase.com) project initialized (in `/supabase`).### Installation
> [!WARNING]
> Projects using React 19 may not work with per-project installation of `srtd` (ref [Ink CLI issue](https://github.com/vadimdemedes/ink/issues/688)). Until resolved, **global installation is recommended.**```bash
# Global installation
npm install -g @t1mmen/srtd# Project installation
npm install --save-dev @t1mmen/srtd# Or run directly
npx @t1mmen/srtd
```### Setup
```bash
cd your-supabase-project
npx @t1mmen/srtd init # Creates srtd.config.json, not required
```### Create Your First Template
Create `supabase/migrations-templates/my_function.sql`:
```sql
DROP FUNCTION IF EXISTS public.my_function; -- Makes it easier to change args later
CREATE FUNCTION my_function()
RETURNS void AS $$
BEGIN
-- Your function logic here
END;
$$ LANGUAGE plpgsql;
```### Development Workflow
1. Start watch mode:
```bash
npx @t1mmen/srtd watch # Changes auto-apply to local database
```2. When ready to deploy:
```bash
npx @t1mmen/srtd build # Creates timestamped migration file
supabase migration up # Apply using Supabase CLI
```> [!TIP]
> To reduce noise in PR's, consider adding `supabase/migrations/*srtd*.sql linguist-generated=true` to your [`.gitattributes` file.](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github) (unless you manually edit the generated files)## The Power of Templates ๐ช
Without templates, the smallest change to a function would show up as a complete rewrite in your version control system. With templates, the diff is clear and concise.
### Perfect For ๐ฏ
โ Database functions:
```diff
-- Event notifications
DROP FUNCTION IF EXISTS notify_changes;
CREATE FUNCTION notify_changes()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify(
'changes',
json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
);
+ RAISE NOTICE 'Notified changes for %', TG_TABLE_NAME; -- Debug logging
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
```โ Row-Level Security (RLS):
```diff
-- Replace/update policies safely
DROP POLICY IF EXISTS "workspace_access" ON resources;
CREATE POLICY "workspace_access" ON resources
USING (workspace_id IN (
SELECT id FROM workspaces
WHERE organization_id = auth.organization_id()
+ AND auth.user_role() NOT IN ('pending')
));
```โ Views for data abstraction:
```diff
CREATE OR REPLACE VIEW active_subscriptions AS
SELECT
s.*,
p.name as plan_name,
p.features
FROM subscriptions s
JOIN plans p ON p.id = s.plan_id
- WHERE s.status = 'active';
+ WHERE s.status = 'active'
+ AND s.expires_at > CURRENT_TIMESTAMP;
```โ Roles and Permissions:
```diff
-- Revoke all first for clean state
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;-- Grant specific access
GRANT USAGE ON SCHEMA public TO authenticated;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO admin;
```โ Safe Type Extensions:
```diff
DO $$
BEGIN
-- Add new enum values idempotently
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'notification_type') THEN
CREATE TYPE notification_type AS ENUM ('email', 'sms');
END IF;-- Extend existing enum safely
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'push';
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'pusher';
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'webhook';
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'whatsapp';
END $$;
```โ Triggers
```diff
DROP TRIGGER IF EXISTS on_new_user ON auth.users;
DROP FUNCTION IF EXISTS public.setup_new_user;CREATE FUNCTION public.setup_new_user() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER
SET search_path = public AS $$
BEGIN
-- Existing logic for new users+ -- Your new changes go here..
END;
$$;CREATE TRIGGER on_new_user AFTER INSERT ON auth.users FOR EACH ROW EXECUTE PROCEDURE public.setup_new_user ();
```> [!TIP]
> You don't need to specifying parameters in drop functions. E.g `DROP FUNCTION IF EXISTS public.my_function;`. This ensures you don't end up with multiple functions with the same name, but different parameters.### Not Recommended For:
* โ Table structures
* โ Indexes
* โ Data modifications
* โ Non-idempotent operationsUse regular [Supabase](https://supabase.com) migrations for these cases.
## Commands ๐ฎ
### Interactive Mode
Running `npx @t1mmen/srtd` without arguments opens an interactive menu. All commands can also be run directly:
- ๐ `srtd watch` - Watch and auto-apply changes
- ๐๏ธ `srtd build [--force] [--bundle]` - Generate migrations from templates
- โถ๏ธ `srtd apply [--force]` - Apply templates directly to local database
- โ๏ธ `srtd register [file.sql...]` - Mark templates as already built
- ๐ `srtd promote - [file.sql ...]` - Promote WIP template to buildable templates
- ๐งน `srtd clean` - Remove all logs and reset config> [!IMPORTANT]
> `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!## Configuration ๐
`srtd.config.json` can be created with `init` command. It is not necessary, if the defaults suit your needs.
```jsonc
{
// Prevents building templates with this extension
"wipIndicator": ".wip",// Migration file naming: 20211001000000_srtd-my_function.sql
"migrationPrefix": "srtd",// Template discovery
"filter": "**/*.sql",// Migration file comments
"banner": "You very likely **DO NOT** want to manually edit this generated file.",
"footer": "",// Wrap migrations in transaction
"wrapInTransaction": true,// File paths
"templateDir": "supabase/migrations-templates",
"migrationDir": "supabase/migrations",
"buildLog": "supabase/migrations-templates/.buildlog.json",
"localBuildLog": "supabase/migrations-templates/.buildlog.local.json",// Database connection
"pgConnection": "postgresql://postgres:postgres@localhost:54322/postgres"
}
```## Other Features ๐ง
### Work in Progress Templates
Add `.wip.sql` extension to prevent migration generation:
```bash
my_function.wip.sql # Only applied locally, never built
```Make a WIP template buildable as migration by renaming it, or using the `promote` command:
```bash
npx @t1mmen/srtd promote my_function.wip.sql
```### Register Existing Objects
Registering a template is useful when you're creating templates for what is already in your database. This avoids generating migrations on `build` (until they're changed)
```bash
# Register specific template
npx @t1mmen/srtd register my_function.sql another_fn.sql# Interactive multi-select UI
npx @t1mmen/srtd register
```This can be useful when setting up `srtd` for an existing project, where you may have hundreds of existing functions, views, etc that you want as templates, but don't want to generate migrations until changed later.
### Template State Management
The state of templates are stored to..
- [`.buildlog.json`](https://github.com/t1mmen/srtd/blob/main/supabase/migrations-templates/.srtd.buildlog.json) - Migration build state (commit this)
- `.buildlog.local.json` - Local database state (add to `.gitignore`)This helps `srtd` identify when templates are changed, to only `build` (as migrations) or `apply` the necessary changes (directly to local db).
## Development ๐ ๏ธ
### Local Setup
```bash
# Clone and install
git clone https://github.com/stokke/srtd.git
cd srtd
npm install# Development
npm run dev # Watch mode
npm test # Run tests
npm start # Run CLI
npm start:link # Build, npm link, and run CLI# Quality Checks
npm run typecheck # Type checking
npm run lint # Lint and fix
npm run format # Format code
npm run test:coverage # Test coverage
```## Contributing ๐ค
While feature-complete for our needs, we welcome:
- ๐ Bug fixes and reliability improvements
- ๐ Documentation improvements
- โ Test coverage enhancements
- โก๏ธ Performance optimizations### Contribution Process
1. Create a [changeset](https://github.com/changesets/changesets) (`npm run changeset`)
2. Ensure tests pass (`npm test`)
3. Follow existing code style
4. Update documentationNote: New features are evaluated based on alignment with project scope.
## Built With ๐ ๏ธ
### Terminal UI
- [Ink](https://github.com/vadimdemedes/ink) - React for CLI interfaces
- [Pastel](https://github.com/vadimdemedes/pastel) - Next-like framework for Ink
- [Figures](https://github.com/sindresorhus/figures) - Unicode symbols
- [Chokidar](https://github.com/paulmillr/chokidar) - File watcher
- [update-notifier](https://github.com/sindresorhus/update-notifier) - Version checks
- [Zod](https://zod.dev/) - Schema validation
- [Conf](https://github.com/sindresorhus/conf) - Config management
- [vhs](https://github.com/charmbracelet/vhs) - Video recording## License
MIT License - see [LICENSE](LICENSE) file.
---
Made with ๐ช by [Timm Stokke](https://timm.stokke.me) & [Claude Sonnet](https://claude.ai)
[](https://www.buymeacoffee.com/t1mmen)