Guide for migrating existing Supabase projects to @gridatek/nx-supabase.
- Prerequisites
- Migration Strategies
- From Standalone Supabase
- From Custom Nx Setup
- Troubleshooting
- Rollback Plan
Before migrating, ensure you have:
- Nx workspace (22+)
- Node.js 18+
- Docker installed and running
- Existing Supabase project(s) backed up
- Git repository with clean working tree
Migrate your existing Supabase project structure to the plugin's conventions.
Pros:
- Preserves git history
- Minimal disruption
- Gradual adoption possible
Cons:
- Requires restructuring files
Create new projects with the plugin and migrate database schema.
Pros:
- Clean structure from day one
- Easier to follow best practices
Cons:
- Loses git history
- More initial work
Typical standalone Supabase project:
my-project/
├── supabase/
│ ├── config.toml
│ ├── migrations/
│ │ ├── 20240101000000_init.sql
│ │ └── 20240102000000_add_posts.sql
│ └── seed.sql
└── package.json
cd my-nx-workspace
npx nx add @gridatek/nx-supabase# Generate new project (creates folder structure)
npx nx g @gridatek/nx-supabase:project my-project
# Or if you want it in apps directory
npx nx g @gridatek/nx-supabase:project my-project --directory=appsThis creates:
my-project/ # or apps/my-project/
├── production/
│ ├── config.toml
│ ├── migrations/
│ └── seeds/
├── local/
│ ├── migrations/
│ └── seeds/
├── .generated/
├── .gitignore
├── README.md
└── project.json
# Backup original
cp -r old-project/supabase old-project/supabase.backup
# Copy config
cp old-project/supabase/config.toml my-project/production/config.toml
# Copy migrations
cp old-project/supabase/migrations/* my-project/production/migrations/
# Copy seeds (if you have seed.sql)
mv my-project/production/migrations/*seed* my-project/production/seeds/ 2>/dev/null || trueEdit my-project/production/config.toml:
# Update project_id to match your naming convention
project_id = "my-project-production"
# Review all settings and adjust as needed
[api]
port = 54321
[db]
port = 54322# Build
npx nx run my-project:build
# Start locally
npx nx run my-project:start
# Verify migrations applied
npx nx run my-project:run-command --command="supabase migration list"
# Test your application
# ... run your tests ...
# Stop when done
npx nx run my-project:stop# If everything works, remove old structure
rm -rf old-project/supabase
# Update your application code to use new paths
# (If you were referencing supabase/ directly)Update your CI/CD pipelines:
Before:
- name: Start Supabase
run: npx supabase start
- name: Run migrations
run: npx supabase db resetAfter:
- name: Build Supabase
run: npx nx run my-project:build
- name: Start Supabase
run: npx nx run my-project:start
- name: Run migrations
run: npx nx run my-project:run-command --command="supabase db reset"If you already have Supabase in an Nx workspace with custom setup:
apps/
└── api/
├── supabase/
│ ├── config.toml
│ └── migrations/
└── project.json # Custom targets
npx nx add @gridatek/nx-supabasecd apps/api
# Create production environment
mkdir -p production
mv supabase/config.toml production/
mv supabase/migrations production/
mv supabase/seeds production/ 2>/dev/null || mkdir production/seeds
# Create local environment
mkdir -p local/migrations
mkdir -p local/seeds
# Create .generated directory
mkdir -p .generated
# Clean up old supabase directory
rm -rf supabaseReplace custom targets with plugin targets (or remove for inferred tasks):
Before:
{
"name": "api",
"targets": {
"supabase:start": {
"executor": "nx:run-commands",
"options": {
"command": "cd apps/api/supabase && supabase start"
}
},
"supabase:stop": {
"executor": "nx:run-commands",
"options": {
"command": "cd apps/api/supabase && supabase stop"
}
}
}
}After (explicit targets):
{
"name": "api",
"targets": {
"build": {
"executor": "@gridatek/nx-supabase:build"
},
"start": {
"executor": "@gridatek/nx-supabase:run-command",
"options": {
"command": "supabase start"
},
"dependsOn": ["build"]
},
"stop": {
"executor": "@gridatek/nx-supabase:run-command",
"options": {
"command": "supabase stop --no-backup"
}
}
}
}Or (inferred tasks - recommended):
Remove the targets entirely and let the plugin infer them. The plugin will detect your project via production/config.toml.
# Add to apps/api/.gitignore
.generated/
.supabase/# Build
npx nx run api:build
# Start
npx nx run api:start
# Verify
npx nx run api:run-command --command="supabase status"
# Stop
npx nx run api:stopUpdate any npm scripts or shell scripts:
Before:
{
"scripts": {
"api:start": "cd apps/api/supabase && supabase start",
"api:migrate": "cd apps/api/supabase && supabase db reset"
}
}After:
{
"scripts": {
"api:start": "nx run api:start",
"api:migrate": "nx run api:run-command --command='supabase db reset'"
}
}If you have separate projects for different environments:
Before:
apps/
├── api-local/
│ └── supabase/
├── api-staging/
│ └── supabase/
└── api-prod/
└── supabase/
After:
apps/
└── api/
├── production/ # From api-prod
├── staging/ # From api-staging
└── local/ # From api-local
# 1. Create unified project
npx nx g @gridatek/nx-supabase:project api --directory=apps
# 2. Migrate production (base config)
cp -r apps/api-prod/supabase/* apps/api/production/
# 3. Migrate staging (only differences)
# Compare configs and copy only what differs
diff apps/api-prod/supabase/config.toml apps/api-staging/supabase/config.toml > staging-diff.txt
# Manually create apps/api/staging/config.toml with only differences
cp apps/api-staging/supabase/migrations/* apps/api/staging/migrations/ 2>/dev/null || true
# 4. Migrate local (only differences)
diff apps/api-prod/supabase/config.toml apps/api-local/supabase/config.toml > local-diff.txt
# Manually create apps/api/local/config.toml with only differences
cp apps/api-local/supabase/migrations/* apps/api/local/migrations/ 2>/dev/null || true
# 5. Remove old projects
rm -rf apps/api-{prod,staging,local}If you have a running Supabase project:
# Dump schema from remote project
npx supabase db dump --linked -f apps/api/production/migrations/20240101000000_initial_schema.sql
# Or from local
npx supabase db dump --local -f apps/api/production/migrations/20240101000000_initial_schema.sql# Apply to new project
npx nx run api:build
npx nx run api:start
npx nx run api:run-command --command="supabase db reset"# Link to remote project
npx nx run api:run-command \
--env=production \
--command="supabase link --project-ref YOUR_PROJECT_REF"
# Pull remote schema
npx nx run api:run-command \
--env=production \
--command="supabase db pull"
# This creates migration files in production/migrations/# Push migrations to remote
npx nx run api:run-command \
--env=production \
--command="supabase db push"{
"scripts": {
"types": "supabase gen types typescript --local > src/types/database.ts"
}
}{
"scripts": {
"types": "nx run api:run-command --command='supabase gen types typescript --local' > libs/types/src/database.ts"
}
}Or create a custom target:
{
"targets": {
"generate-types": {
"executor": "nx:run-commands",
"options": {
"command": "nx run api:run-command --command='supabase gen types typescript --local' > libs/types/src/database.ts"
},
"dependsOn": ["build"]
}
}
}Problem: "No migrations found in production/migrations/"
Solution:
# Check file locations
ls -la apps/api/production/migrations/
# Ensure files match pattern: YYYYMMDDHHMMSS_name.sql
# Rename if needed:
mv old_migration.sql 20240101000000_old_migration.sqlProblem: "Config file not found at production/config.toml"
Solution:
# Generate default config
npx supabase init
# Move to production directory
mv supabase/config.toml apps/api/production/
# Update project_id
sed -i 's/project_id = ".*"/project_id = "api-production"/' apps/api/production/config.tomlProblem: "Port 54321 already in use"
Solution:
# Stop old Supabase instances
docker ps | grep supabase | awk '{print $1}' | xargs docker stop
# Or change port in config.toml
[api]
port = 54322Problem: "Docker daemon not running"
Solution:
# Start Docker
# On macOS: open -a Docker
# Verify Docker is running
docker ps
# Reset Docker if needed
docker system prune -aIf migration fails, you can rollback:
npx nx run api:stop# Restore from backup
cp -r old-project/supabase.backup old-project/supabase
# Or restore from git
git checkout HEAD -- old-project/supabasecd old-project/supabase
npx supabase startReview error logs and revisit migration steps.
- All migrations run successfully
- Application connects to Supabase
- Tests pass
- CI/CD updated
- Team documentation updated
- Old structure removed
- Git history clean (committed changes)
- Team members notified of changes
If you encounter issues during migration:
- Check the API Reference
- Review Best Practices
- Search GitHub Issues
- Create a new issue with:
- Current structure
- Attempted migration steps
- Error messages
- Nx version (
npx nx --version) - Node version (
node --version)
my-app/
├── package.json
└── supabase/
├── config.toml
├── migrations/
│ ├── 20240101_init.sql
│ └── 20240102_posts.sql
└── seed.sql
my-workspace/
├── apps/
│ └── my-app/
│ ├── production/
│ │ ├── config.toml
│ │ ├── migrations/
│ │ │ ├── 20240101000000_init.sql
│ │ │ └── 20240102000000_posts.sql
│ │ └── seeds/
│ │ └── initial_data.sql
│ ├── local/
│ │ ├── migrations/
│ │ └── seeds/
│ │ └── dev_data.sql
│ ├── .generated/
│ ├── .gitignore
│ ├── README.md
│ └── project.json
├── nx.json
└── package.json
# 1. Create Nx workspace (if needed)
npx create-nx-workspace@latest my-workspace --preset=apps
# 2. Add plugin
cd my-workspace
npx nx add @gridatek/nx-supabase
# 3. Generate project
npx nx g @gridatek/nx-supabase:project my-app --directory=apps
# 4. Copy files
cp ../my-app/supabase/config.toml apps/my-app/production/
cp ../my-app/supabase/migrations/* apps/my-app/production/migrations/
cp ../my-app/supabase/seed.sql apps/my-app/production/seeds/initial_data.sql
# 5. Test
npx nx run my-app:build
npx nx run my-app:start
npx nx run my-app:run-command --command="supabase status"
# 6. Success! Clean up
rm -rf ../my-app/supabaseFor ongoing usage, see the Best Practices Guide.