Features Apograph CMS on GitHub

Upgrading

Bump every @apograph/* package to one version, migrate forward, and know what each release changed.

Documents 0.5.2 Updated Edit this page Report a problem

On this page

Every @apograph/* package is released together, at one version, and a generated app pins each of them exactly with no caret. An upgrade is therefore one number changing in many places at once, followed by apograph migrate.

The lockstep rule

The packages resolve each other by name, and several of them share a dependency they must hold one copy of — React contexts in the admin, the design system’s tokens, the bootstrap host’s plugin contract. Two versions of @apograph/design-system in node_modules means two React context instances, and an admin whose sidebar silently stops talking to its provider.

Upgrade the whole set, or none of it

A partial upgrade errors nowhere. The UI simply stops working in a way that looks like a bug in your own code, and npm ls react is what finds it.

Bumping

List the packages you have, and install every one of them at the new version in a single npm install:

NEW=0.5.2
npm install $(node -e '
    const m = require("./package.json");
    const names = Object.keys({ ...m.dependencies, ...m.devDependencies })
        .filter((n) => n.startsWith("@apograph/"));
    console.log(names.map((n) => n + "@" + process.argv[1]).join(" "));
' "$NEW")

One command, so npm resolves the whole set at once and does not stage a tree with half of it moved. Then confirm there is exactly one copy of the shared packages:

npm ls @apograph/design-system react

Two entries for either is a partial upgrade — fix it before booting.

Adding a package you did not have — the GraphQL adapter, a storage provider, a copilot backend — follows the same rule: install it at the version the rest are on, never @latest on its own.

Migrate, forward

npx apograph migrate

It builds the server first, then reads the compiled config and plugin list and applies every plugin’s pending migrations in plugin-list order. Run it as a release step, once, before the new version starts — not at boot, where every replica would run it in parallel.

Migrations are forward-only. There is no down. A rollback is a deploy of the previous application version against a database that has already migrated, which works for an additive migration and does not for a destructive one. Read the SQL a release adds before you plan the rollback, and take a backup before you run it.

The release order is the same every time:

  1. Bump the set, npm install
  2. apograph build
  3. apograph migrate
  4. Start the new version
  5. Stop the old one

What each release changed

Read down from your current version. Only changes that need something from you are listed; the changelog has the rest.

0.5.2

No tables and no API change: the fix is to create-apograph-app, so upgrading an existing app needs nothing. It is worth reading anyway if your app was generated by 0.5.1 or earlier, because that app carries five settings that do nothing.

Its .env documents WEBHOOKS_DELIVERY_INTERVAL, WEBHOOKS_TIMEOUT, WEBHOOKS_RETENTION_DAYS, WEBHOOKS_ALLOW_INSECURE_URLS and WEBHOOKS_ALLOW_PRIVATE_NETWORKS — but the generated plugins.ts registered WebhooksPlugin() with no argument and no config/webhooks.ts existed to read them. Setting one changed nothing, silently: an unread environment variable is not an error. The two ALLOW flags are the ones that bite, because turning either on is how you reach a receiver on plain HTTP or inside your own network, and it appeared to have no effect.

ChangeWhat to do
Newly generated apps get a config/ module for webhooks, transfer, segments and GraphQL, and pass each to its plugin.Nothing for a new app.
An app generated before 0.5.2 ignores every WEBHOOKS_* key in its own .env.Add a config/webhooks.ts that reads them and pass it: WebhooksPlugin(config.plugins.webhooks). The quickest way to get the file is to scaffold a throwaway app with 0.5.2 and copy it across.
The same app passes no transfer identity map, no reader-tag resolver and no GraphQL cost budget.Optional — all three have working defaults. Copy those modules over at the same time if you want the settings to have a home.

0.5.1

Packaging only — a release staged one package without its licence field and one build’s output was not what shipped. Nothing to do beyond the bump.

0.5.0

Three plugins add tables, so apograph migrate is not optional: webhooks brings its own three, the activity log gains an actor type and a workspace column, and the outbox gains a retry backoff and a last-error column.

ChangeWhat to do
Webhooks ships as a plugin. An existing app does not get it by upgrading.Install @apograph/webhooks-server and @apograph/webhooks-admin at the same version, then register WebhooksPlugin(config.plugins.webhooks) on the server and WebhooksPlugin() in the admin. See the plugin page.
The activity log now records the contexts that used to raise no event at all, and one entry’s history is reachable from the entry.Nothing, beyond the migration. Existing rows keep their meaning.
apograph --version answers, and apograph --help no longer exits 1.Nothing.

0.4.1 – 0.4.3

No breaking changes. 0.4.3 adds tables — alarms, reader segments, saved list views — so apograph migrate is not optional. create-apograph-app fixes in 0.4.1 and 0.4.2 affect only apps scaffolded afterwards.

0.4.0

ChangeWhat to do
SESSION_SECRET and TOKEN_SECRET removed from IdentityPlugin’s config. They were read by nothing — sessions and one-time tokens are random values checked against a row.Delete both from .env and from apograph.config.ts. A config object that still passes sessionSecret or tokenSecret will not type-check.
MediaServerPlugin takes one constructed provider, not a providers array. There is no registry, no resolver and no defaultProvider.Construct the one backend in plugins.tscreateLocalStorageProvider(config), createS3StorageProvider(config), and so on — and pass it as provider.
Storage backends are their own packages: @apograph/media-provider-local, -s3, -azure, -gcs, -vercel-blob, and -memory for tests.Install the one you use, at the same version as the rest. MEDIA_PROVIDER was documented and never existed; remove it.
Signed-URL direct serve. MEDIA_DIRECT_SERVE=signed-url makes /raw answer a 302 on backends that can sign.Clients that follow redirects need nothing. curl needs -L.
The apograph CLI and npx create-apograph-app arrive.Existing apps can adopt @apograph/cliapograph build, migrate, start — but nothing forces it.
Single sign-on. IdentityPlugin(config, { sso: { providers } }) with session.cookieSameSite: 'strict' refuses to boot.Use lax if you register an identity provider.
The transfer plugin ships but a scaffolded plugins.ts does not register it.Add TransferPlugin server-side and its admin half to get Export and Import.

0.3.0

ChangeWhat to do
richtext values are a structured document, not a string. Rows written before 0.3.0 still hold strings.Anything reading a rich-text value must accept both — asRichTextDocument and richTextPlainText in @apograph/content-domain normalise either.
A media value carries alt text.Readers that treated a media field as a bare id should read the expanded media block instead.
CopilotPlugin drops defaultProvider. The provider list is the setting: the first registered backend serves a run that names none.Order providers in plugins.ts deliberately, and remove defaultProvider from the config.
TRUST_PROXY is read by the host. Unset, forwarded headers are ignored.Set it to the hop count — TRUST_PROXY=1 — on any deployment behind a proxy. Before 0.3.0 the login rate limit was already collapsing into one bucket behind a proxy; now it can be fixed.
Entry domain events (entry.created, entry.updated, entry.deleted, entry.restored) join the outbox.Nothing, unless you wrote an outbox subscriber that assumed only publish events existed.

Webhooks

A freshly scaffolded app registers the webhooks plugin. An app scaffolded before the plugin existed does not gain it by bumping the version: install @apograph/webhooks-server and @apograph/webhooks-admin at the set’s version, add WebhooksPlugin() to plugins.ts and to the admin’s plugin list, and run npx apograph migrate — it owns three tables.

@apograph/copilot-provider-fake is no longer published. A deployment that registered it as an offline fallback must configure a real backend or leave COPILOT_ENABLED=false.

  • Deployment — the release order in the context of the whole process, and the reverse proxy.
  • Database and migrations — the plugin-order constraint a failed migration usually means.
  • Troubleshooting — the symptom a partial upgrade produces, and the others.