Dyrected
Deployment & OperationsInfrastructureDatabase Adapters

Migrations

How Dyrected keeps your database in step with your schema — automatic schema sync, safe field renames, and pushing schema to Dyrected Cloud.

Schemas change. You add a collection, rename a field, give something a default. In a lot of tools that means writing migration files by hand. Dyrected takes a different approach: it keeps the database schema in step with your config automatically, and gives you a few field-level tools for the changes that need care. There is no migration-file workflow and no migrate command to learn.

It helps to separate two things that both get called "migrations":

  • Schema changes — the tables and columns your database needs. Dyrected handles these for you.
  • Content-contract changes — renaming a field, adding a default, reshaping data that already exists. These are yours to manage, with the tools below.

Automatic schema sync

On startup, the SQL adapters (Postgres, SQLite, MySQL) bring the database in line with your config. They create a table for any collection that doesn't have one yet, and add a column for any newly promoted field. Add a collection or a promoted field, restart, and the schema is ready.

This sync is deliberately additive. It creates missing tables and columns; it does not drop columns, rename them, or change their types. That keeps your data safe by default, and it's why the destructive changes are the ones you have to think about. MongoDB is schema-flexible, so it needs no sync step at all.

Renaming a field safely

Renaming a field is the classic risky change: the new name has no data, and the old data is stranded under the old key. Dyrected handles this with renameTo plus a default. Rather than rewriting every document at once, it reads the old value under the new name whenever a document is loaded.

Set the field's name to the new key, and point renameTo at the previous key it should fall back to:

import { defineCollection, defineTextField } from '@dyrected/core'

export const Customers = defineCollection({
  slug: 'customers',
  fields: [
    defineTextField({
      name: 'fullName',
      label: 'Full name',
      renameTo: 'name',
      defaultValue: '',
      required: true,
    }),
  ],
})

Here the field is now called fullName, and renameTo: 'name' tells Dyrected to read the old name value when a document has no fullName yet. This happens at read time: load a document and the old value surfaces under the new name, so your app and the admin panel see the right value straight away. The value is physically rewritten under the new key the next time that document is saved with the new field present — for example, when someone edits and saves it in the admin panel. Documents nobody touches keep their value under the old key, which is exactly why you leave renameTo in place.

Keep renameTo until you've confirmed every production document has been written under the new key. Remove it too early and documents that still only have the old key will look empty.

Defaults and backfilling

defaultValue fills in a value when a document doesn't have one, which makes it the natural partner to a rename and a safe way to introduce a new required field:

defineSelectField({ name: 'status', label: 'Status', options: ['draft', 'published'], defaultValue: 'draft' })

Some field types also fall back to a sensible empty value when nothing is set — booleans default to false and array fields default to an empty list — so existing documents read predictably even before they're rewritten.

Promoting a field on an existing collection

Adding promoted: true to a field that already has data adds the column on the next startup, and every new write populates it. Existing rows are not backfilled: their value stays in the JSON body, and the new column is left empty for them.

Reads still return the right value, because Dyrected reads the document body rather than the promoted column. Filtering and sorting, though, use the column — so a query on a freshly promoted field only "sees" the rows written since promotion. For a large existing table, plan a rewrite of the affected documents if you need every row covered — for example, load and re-save them so each write fills the new column.

Pushing your schema to Dyrected Cloud

Self-hosted apps sync their schema at startup. Cloud projects push it explicitly with the CLI:

npx dyrected sync:schema

This sends your collections, globals, and admin config to your Cloud site. It reads your API key and site ID from DYRECTED_API_KEY and DYRECTED_SITE_ID (or --api-key and --site-id flags), and regenerates your TypeScript types afterward unless you pass --skip-types. In CI you can pass --skip-on-error so a failed sync doesn't fail the build.

Before a production schema change

Whichever adapter you use, treat a schema change in production with care:

  1. Back up the database using your provider's recovery procedure.
  2. Review new promoted-field types and any uniqueness expectations against the values already stored.
  3. Run the change in a staging environment with production-like data first.
  4. Deploy code that can read both the old and new shape during a rolling release, so requests served mid-deploy don't break.

For the deployment side of this, see Deployment.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend