Migrations
Migrations keep your collections (and any data or settings they need) in code, so every installation gets the same schema: your laptop, staging, production, and each new server. They're JavaScript files, applied in order, each in a transaction. (Migrations written for PocketBase run too; see below.)
[!NOTE] Migrations need the JavaScript engine (the default build).
- Writing a migration
- When they run
- Automigrate
- The command line
- What's available
- Coming from PocketBase
#Writing a migration
A migration is a . file in the migrations directory (slonbase_migrations, see
configuration). It calls migrate( with an up function, which makes
the change, and optionally a down function, which undoes it:
// slonbase_migrations/1730000000_create_posts.js
migrate((app) => {
const posts = new Collection({
name: "posts",
listRule: "",
viewRule: "",
fields: [
{ name: "title", type: "text", required: true, max: 200 },
{ name: "published", type: "bool" },
],
});
app.save(posts);
}, (app) => {
app.delete(app.findCollectionByNameOrId("posts"));
});slonbase migrate create create_posts writes an empty one with the right name. Files run in
the order of their names, which start with a Unix timestamp, so new ones go last.
Each migration runs in a transaction of its own. Everything it does applies together, or,
if it throws, not at all, and the migrations after it wait. That covers collection changes,
records and raw SQL alike. A call that fails inside a try doesn't spoil the transaction: the
migration can catch the error and go on.
#When they run
The server applies pending migrations when it starts, before it answers requests. Each file
runs once: the applied ones are kept in the database (_slonbase.), so they're
applied once per database, not once per server.
- Several instances starting at once apply each migration once.
- A failing migration keeps the server starting.
/answersapi/ health 503, and the error is its status and in the log. slonbase tries again with a growing delay (up to a minute), so fixing the file is enough: no restart needed. - After the setup wizard, pending migrations run before the dashboard opens.
- Backups include the list of applied migrations, so a restored backup and its migrations stay in step.
- Projects each have their own migrations, in
<projects..path>/ <name>/ migrations
#Automigrate
With automigrate on (--automigrate, SLONBASE_AUTOMIGRATE=true or
migrations.), every collection change you make in the dashboard or through
the collections API is also written as a migration file:
slonbase_migrations/1730000123_created_posts.js
slonbase_migrations/1730000187_updated_posts.js
slonbase_migrations/1730000201_deleted_drafts.jsThe files are marked as applied, since the changes already are. Commit them, and the other
installations get the changes when they start. Each file holds the whole collection definition
before and after, so down restores the previous version.
Automigrate is off by default. Turn it on where you design the schema (usually your own machine), and keep it off in production, where the changes come from the files.
#The command line
slonbase migrate list # every file, applied or pending
slonbase migrate up # apply the pending ones (the server also does)
slonbase migrate down # revert the last one; `down 3` reverts the last three
slonbase migrate create add_tags # a new, empty migration
slonbase migrate collections # a snapshot: one migration that recreates every collection
slonbase migrate history-sync # forget applied migrations whose files you deletedUse the same --config (or environment) as the server, and --project <name> for a project.
For the packages, run them as the service user:
sudo -u slonbase slonbase --config /.
migrate collections is handy to start using migrations on an existing installation: the
snapshot describes the collections as they are, so it's marked as applied here and creates
them on a new database.
#What's available
Migrations get app, the same API as $app in hooks, with
every call running in the migration's transaction.
Collections
new Collection( | A collection definition, in the collections API's JSON (name, type, fields, rules, indexes, auth options, viewQuery) |
app. | Creates it, or updates the collection with its id (or name). Validated like the API. |
app. | Deletes it with its records |
app., app. | |
app. | Creates or updates several at once; with deleteMissing, deletes the other non-system collections |
collection. | A list of fields with add(, addAt(, getById, getByName, removeById, removeByName, fieldNames( |
new Field(, new TextField(, NumberField, BoolField, EmailField, URLField, EditorField, DateField, AutodateField, SelectField, FileField, RelationField, JSONField, GeoPointField, PasswordField | Fields, in the API's JSON |
unmarshal( | Copies JSON keys onto a collection (or a record) |
Records and data: new Record(, app., app.,
app., app., app.,
app., app., app., and
app. with $dbx for SQL (PostgreSQL; see SQL). Collections are
tables in the slonbase schema, and unqualified names find them.
Settings: const settings = app.
For example, a first superuser and some data:
migrate((app) => {
const superuser = new Record(app.findCollectionByNameOrId("_superusers"));
superuser.set("email", "admin@example.com");
superuser.set("password", $os.getenv("ADMIN_PASSWORD"));
app.save(superuser);
app.db().newQuery("UPDATE posts SET published = true WHERE title <> ''").execute();
});Notes:
- View collections can be created from migrations without the SQL console: migrations are code you deploy, like hooks.
- Collection hooks don't run for changes made by migrations. Record hooks do when the server
applies them, but not with
slonbase migrate up, which doesn't load the hooks. e., routes and cron jobs aren't available in migrations.next( )
#Coming from PocketBase
Copy pb_migrations/ to the migrations directory. The files PocketBase writes (its
automigrate output and pocketbase migrate collections snapshots) are tested against slonbase
in CI.
slonbase import-pocketbasebrings the history along: PocketBase's applied migrations are marked as applied, so they don't run again on the imported collections.- PocketBase's system collections in snapshots and imports:
_superusersand_externalAuthstake the settings (rules, auth options) but keep slonbase's fields, and_mfas,_otpsand_authOrigins(tables of slonbase's own) are skipped. - SQL is PostgreSQL in
app.queries and view queries.db( ) - Migrations written for PocketBase before 0.23 (
migrate() use an API that PocketBase itself replaced; rewrite them with( db) => { const dao = new Dao( db) . . . app.