Summary
Add a new RPC method to the schema engine that returns structured, machine-readable diff output instead of only human-readable text or SQL scripts.
Motivation
Currently, migrateDiff only outputs human-readable text or SQL scripts to stdout. This makes it difficult to programmatically analyze schema changes without brittle regex parsing. Several use cases would benefit from structured diff output:
- Auto-generating migration names based on schema changes (e.g.,
add_email_to_User, create_table_Post)
- Building diff visualization tools that need to understand what changed
- Automated schema analysis for CI/CD pipelines
- Custom migration workflows that need to inspect changes before applying
Current Limitations
The existing diff RPC method:
- Returns only an exit code in the JSON response
- Outputs human-readable text via "print" requests to stdout
- Requires parsing text output with regex (brittle and error-prone)
- Text format may change between versions, breaking tooling
Proposed Solution
Add a new RPC method diffStructured (or add a parameter to existing diff method) that returns structured JSON output.
Proposed API
// RPC method name: "diffStructured"
interface DiffStructuredInput {
from: MigrateDiffTarget
to: MigrateDiffTarget
shadowDatabaseUrl: string | null
filters: SchemaFilter
}
interface DiffStructuredOutput {
changes: SchemaChange[]
exitCode: MigrateDiffExitCode
}
type SchemaChange =
| AddTableChange
| DropTableChange
| RenameTableChange
| AddColumnChange
| DropColumnChange
| AlterColumnChange
| AddIndexChange
| DropIndexChange
| AddForeignKeyChange
| DropForeignKeyChange
// ... other change types
interface AddTableChange {
type: 'AddTable'
table: string
columns: ColumnDefinition[]
}
interface DropTableChange {
type: 'DropTable'
table: string
}
interface AddColumnChange {
type: 'AddColumn'
table: string
column: string
columnType: string
nullable: boolean
default?: string
}
interface DropColumnChange {
type: 'DropColumn'
table: string
column: string
}
interface AlterColumnChange {
type: 'AlterColumn'
table: string
column: string
changes: ColumnPropertyChange[]
}
type ColumnPropertyChange =
| { property: 'type', from: string, to: string }
| { property: 'nullable', from: boolean, to: boolean }
| { property: 'default', from: string | null, to: string | null }
// ... similar structures for indexes, foreign keys, etc.
Example Output
{
"changes": [
{
"type": "AddColumn",
"table": "User",
"column": "email",
"columnType": "String",
"nullable": false
},
{
"type": "AddTable",
"table": "Post",
"columns": [
{
"name": "id",
"type": "Int",
"nullable": false,
"autoIncrement": true
},
{
"name": "title",
"type": "String",
"nullable": false
}
]
}
],
"exitCode": 2
}
Benefits
- Robust tooling: No regex parsing needed, changes are strongly typed
- Version stability: JSON structure can be versioned and evolved
- Better UX: Enables features like auto-generated migration names
- Ecosystem growth: Third-party tools can build on structured diff data
- Backward compatible: Existing
diff method continues to work
Use Cases
1. Auto-generate migration names
const diff = await engine.diffStructured({ from, to })
const name = generateMigrationName(diff.changes)
// e.g., "add_email_to_User" or "create_table_Post"
2. Diff visualization
const diff = await engine.diffStructured({ from, to })
renderDiffUI(diff.changes) // Show structured diff in UI
3. CI/CD validation
const diff = await engine.diffStructured({ from, to })
const hasBreakingChanges = diff.changes.some(c =>
c.type === 'DropColumn' || c.type === 'DropTable'
)
if (hasBreakingChanges) {
throw new Error('Breaking changes detected')
}
Implementation Considerations
- The schema engine already computes these changes internally for generating SQL
- This would expose existing internal data structures via RPC
- Could reuse existing diff logic, just format output differently
- Should support all database providers (PostgreSQL, MySQL, SQLite, etc.)
- Consider adding this as a new RPC method to avoid breaking existing
diff behavior
Related Issues
Alternatives Considered
- Parse human-readable output: Brittle, breaks when format changes
- Parse SQL output: Database-specific, complex to parse correctly
- Use evaluateDataLoss warnings: Only contains data loss warnings, not all changes
None of these alternatives provide a robust, maintainable solution.
Questions
- Would the team be open to this enhancement?
- Should this be a new RPC method or a parameter on existing
diff?
- What level of detail should be included in the change structures?
- Any concerns about exposing internal diff structures?
I'm happy to contribute to the implementation if the team is interested, though I'd need guidance on the Rust codebase structure.
Summary
Add a new RPC method to the schema engine that returns structured, machine-readable diff output instead of only human-readable text or SQL scripts.
Motivation
Currently,
migrateDiffonly outputs human-readable text or SQL scripts to stdout. This makes it difficult to programmatically analyze schema changes without brittle regex parsing. Several use cases would benefit from structured diff output:add_email_to_User,create_table_Post)Current Limitations
The existing
diffRPC method:Proposed Solution
Add a new RPC method
diffStructured(or add a parameter to existingdiffmethod) that returns structured JSON output.Proposed API
Example Output
{ "changes": [ { "type": "AddColumn", "table": "User", "column": "email", "columnType": "String", "nullable": false }, { "type": "AddTable", "table": "Post", "columns": [ { "name": "id", "type": "Int", "nullable": false, "autoIncrement": true }, { "name": "title", "type": "String", "nullable": false } ] } ], "exitCode": 2 }Benefits
diffmethod continues to workUse Cases
1. Auto-generate migration names
2. Diff visualization
3. CI/CD validation
Implementation Considerations
diffbehaviorRelated Issues
Alternatives Considered
None of these alternatives provide a robust, maintainable solution.
Questions
diff?I'm happy to contribute to the implementation if the team is interested, though I'd need guidance on the Rust codebase structure.