Skip to article body
A man carrying a tall stack of paper through an old Cairo print shop, the sheets lifting and smeared by motion
A man carrying a tall stack of paper through an old Cairo print shop, the sheets lifting and smeared by motion

Web Development · Process

Ten Empty Articles

What Prismic's Migration API does between creating a document and filling it.

By Shehab Waleed, Founder, Cairo Studio 3 min read

We write most of our Prismic content from scripts. Journal posts, translations and project pages go through the Migration API, and a publish ships them without anyone opening the dashboard. It is fast and repeatable, and one time it put ten blank articles on our live site.

What happened

One run was creating ten new journal posts and updating older ones. An update threw Assets not found, because those older posts still pointed at stock author portraits we had deleted from the media library. The script stopped there. The next publish shipped what was in the release, and that included ten new documents with nothing in them.

Every one of those pages answered 200. An uptime check would have passed.

Why the documents were empty

Documents can link to each other, so client.migrate() works in passes. It has to create every document before it can resolve those links. You can read this in the client source, version 7.22.

await this.migrateCreateAssets(migration, params);
await this.migrateCreateDocuments(migration, params);
await this.migrateUpdateDocuments(migration, params);

The second pass creates each document with data set to an empty object. The third fills it in. Our failure happened in the third pass, after the empty documents already existed.

You cannot look before you publish

The obvious safeguard is to read the drafts back and check them, but drafts cannot be read on any ref. Publishing is all or nothing: it ships everything in the migration release, and no endpoint lists what is in there. The counter it returns is not reliable either. We have seen it report 0 for a publish that shipped three documents.

So the checks have to happen before the write and after the publish, and a failed run has to stop everything in between.

try {
await client.migrate(migration);
} catch (err) {
console.error('release may hold empty documents', err);
process.exit(1);
}

await client.publishMigrationRelease();

If that catch ever runs, finish the fill before anything else publishes. The release is shared by every script that writes to the repository.

Three more sharp edges

Updates start from master

migration.updateDocument() patches the published copy. Run two migrations on the same document without a publish between them, and the second silently discards the changes from the first. Every field change for a document has to ride in one migration.

The CDN holds on to the old ref

Right after a publish, the CDN can keep serving the previous master ref, which looks exactly like a publish that did nothing. Read from the origin host instead, and allow for a delay, since publishing is asynchronous.

const api = await fetch(
`https://${REPO}.prismic.io/api/v2`
).then((r) => r.json());

const ref = api.refs.find((r) => r.isMasterRef).ref;

Foreign assets fail

Passing image URLs and letting Prismic fetch them failed on us with Could not fetch foreign asset. We now download every image first, cache the bytes on disk and upload those, so a re-run costs nothing.

If you are still choosing a platform, our comparison of custom builds and site builders covers what comes before any of this.

Share
X LinkedIn Facebook WhatsApp Email
Hands spreading bare periwinkle sheets across a table, the fingers smeared by motion

Ten pages, all of them blank.

A script prints what it would create and exits. Nothing is written without an explicit flag.

Each document is checked against the custom type JSON first, so a renamed field fails early, before assets are uploaded.

When updating an older document, empty any asset field that points at a deleted file, because that is what stopped our run.

A thrown error ends the script. Nothing publishes on top of a partial run.

After a delay, read the master ref from the origin and check that the new documents have real content.

client.migrate() creates documents with empty data, then fills them in a later pass. If that pass throws, the documents stay empty. Rerun the fill before you publish.

Not through the API. Drafts cannot be read back on any ref and the release cannot be listed. You can review in the dashboard, or validate before writing and verify after publishing.

Publishing is asynchronous and the CDN caches the master ref. Wait a moment, then read from the origin host rather than the cdn host.

We model content, write the migration and build the site on top of it, in Prismic and other headless systems.

More from the Journal

view all →