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.
