Google Cloud Spanner Migrations Runner
The `google-spanner-migrations-runner` package provides a robust engine for managing schema migrations in Google Cloud Spanner databases, supporting both Google-managed instances and the Spanner emulator. Currently at version 1.13.0, the library receives frequent updates, with minor versions and bug fixes typically released every few months. Unlike ORM-based migration tools, this runner operates directly with `.sql` files, emphasizing a 'migrations-as-code' approach where developers write and test their SQL scripts. It does not generate schema from application code. Key functionalities include applying migrations from a designated directory, validating SQL files, and maintaining a ledger of applied migrations to prevent re-execution. Migrations are applied transactionally and in a specific order, making file naming conventions crucial for successful database evolution. It also features optional annotations for environment-specific migration execution.
Common errors
-
Error: Migration file '<filename>' does not match expected pattern.
cause The name of a `.sql` migration file does not conform to the required `[0-9]{5}[a-z\-]{0,256}.sql` format.fixRename your migration `.sql` files to follow the pattern, for example: `00001_create_users_table.sql`. -
Error: Migration '<filename>' contains multiple types of statements. Each migration must contain only one type.
cause A single `.sql` migration file includes a mix of different SQL statement types, such as DDL (e.g., CREATE TABLE) and DML (e.g., INSERT INTO).fixSeparate your migration logic into distinct `.sql` files, ensuring each file contains only one type of SQL operation. For instance, put all schema changes in one file and data inserts in another. -
Error running Spanner migrations: RpcError: 9 FAILED_PRECONDITION: Row '...' in table '...' was already applied.
cause An attempt was made to apply a migration that has already been recorded as applied in the internal migrations tracking table.fixThis usually indicates a successful prior run. If you intend to run new migrations, ensure new `.sql` files are added or that existing applied migrations were not altered. -
WARNING: Migration '<filename>' ignored in emulator due to unsupported feature.
cause The SQL within the migration file contains statements or features (e.g., specific index types, certain DDL) that are not supported by the Google Cloud Spanner emulator.fixThis is often expected in emulator environments. Review Spanner emulator documentation for unsupported features or consider testing these migrations against a live Spanner instance. The migration will still apply on a real Spanner database.
Warnings
- gotcha Each SQL statement within a migration file must be explicitly separated by a semicolon (`;`). The runner relies on this delimiter to parse and execute individual queries.
- gotcha Each migration `.sql` file MUST contain only one type of SQL statements (e.g., all DDL or all DML). Mixing statement types can lead to parsing errors or unexpected behavior.
- gotcha Once a migration is applied, its file name MUST NOT be changed, as the engine tracks applied migrations by name. Similarly, do not modify already applied migration files.
- gotcha Certain Spanner features (e.g., specific index types, some DDL statements) are not fully supported by the Spanner emulator. The runner may ignore these in an emulator environment, logging a warning.
- gotcha Migrations using the `-- @only-in-env <env>` annotation will result in a 'failed' entry in the migrations table if the runner's `--env` option (or programmatic `config.env`) does not match the specified environment or is omitted, rather than simply being skipped.
Install
-
npm install google-spanner-migrations-runner -
yarn add google-spanner-migrations-runner -
pnpm add google-spanner-migrations-runner
Imports
- SpannerMigration
const SpannerMigration = require('google-spanner-migrations-runner');import { SpannerMigration } from 'google-spanner-migrations-runner'; - MigrationConfig
import type { MigrationConfig } from 'google-spanner-migrations-runner'; - runMigrations
import { SpannerMigration } from 'google-spanner-migrations-runner'; const runner = new SpannerMigration(config); await runner.runMigrations();
Quickstart
import { SpannerMigration } from 'google-spanner-migrations-runner';
// A minimal example demonstrating how to programmatically run migrations.
// Ensure you have a 'migrations' directory with .sql files in your project root.
async function main() {
// Configure Spanner connection and optional environment for annotations.
const config = {
projectId: process.env.SPANNER_PROJECT_ID ?? 'your-gcp-project-id',
instanceId: process.env.SPANNER_INSTANCE_ID ?? 'your-spanner-instance-id',
databaseId: process.env.SPANNER_DATABASE_ID ?? 'your-spanner-database-id',
migrationsDir: './migrations', // Path to your SQL migration files
env: process.env.NODE_ENV ?? 'development' // Optional, for @only-in-env annotations
};
// Basic validation for demonstration. In a real app, use proper error handling.
if (!config.projectId || !config.instanceId || !config.databaseId) {
console.error('Missing Spanner configuration. Set SPANNER_PROJECT_ID, SPANNER_INSTANCE_ID, SPANNER_DATABASE_ID environment variables.');
process.exit(1);
}
try {
const runner = new SpannerMigration(config);
console.log('Starting Spanner migrations...');
await runner.runMigrations();
console.log('Spanner migrations completed successfully.');
} catch (error) {
console.error('Error running Spanner migrations:', error);
process.exit(1);
}
}
main();
// Example content for 'migrations/00001_initial_schema.sql':
// CREATE TABLE users (
// id INT64 NOT NULL,
// name STRING(MAX)
// ) PRIMARY KEY (id);