Built with Chinaski

Background Jobs

For very large content batches — hundreds of pages, posts, or block inserts — the synchronous create API isn’t the right tool. Chinaski ships a job queue: you submit one request describing the whole batch, a worker processes it item by item in the background, and you poll a single endpoint (or glance at the admin Jobs screen) until the counters settle.

Submitting a job

POST /api/jobs with a write-scoped API key and a JSON body of {type, items}:

  • type — exactly one of three supported kinds:
    • bulk_upsert_pages — create-or-update pages
    • bulk_upsert_posts — the same for posts
    • bulk_create_blocks — create new blocks
  • items — a non-empty array of row objects matching the kind you chose.

The endpoint answers immediately with {"job_id": "…", "status": "queued"}. Every item gets processed one after the other in a background worker, so a 5,000-item batch costs you one small request instead of one giant one.

Item shape (all three types)

For page/post upserts, each item needs at minimum:

  • slug + language — the pair that identifies the item.
  • If no existing row matches that pair: also title (required for creation), plus whatever other fields you want set — only real page/post columns are honored.
  • If a row does exist: any supplied fields update it in place, except slug and language — those are the identity and can never be modified by the job.
  • Items with published=true are created published; everything else stays a draft until something publishes it.

For blocks, items are plain create records (fields as create_block takes them).

Following a job

  • Poll GET /api/jobs/:id (read-scoped API key). The response shape is fixed:
{
  "job_id": "…", "type": "bulk_upsert_pages",
  "status": "queued | running | done | failed",
  "progress": { "total": 500, "done": 312, "errors": 7 },
  "created_at": "…", "updated_at": "…",
  "result": { "completed": 493, "errors": [  ] }
}
  • The counters tick live while the job runs — done goes up (or errors for items that failed) in real time; the result object appears exactly when the status flips to done.
  • Item-level failures don’t fail the job: the row keeps moving, and the final result lists each error along with the original item so you can fix and re-run just those rows.
  • Statuses are literal — a crashed worker is marked failed (never left stuck in queued), so polling always converges.

Retrying failed items

Jobs don’t retry themselves. Take the finished job’s result.errors array, fix the failing rows (duplicate slugs, missing title, blocked language), and submit a new job containing only those items — upsert semantics mean already-succeeded items are simply updated again, idempotently.

The admin Jobs screen

The Jobs screen in the admin (Administrators only) shows the most recent 200 jobs, newest first, with the type, live progress counters, and any result/error snapshot, timestamps in UTC. It’s read-only: reload to refresh, and no submit/controls exist there — jobs are an API surface by design, so the screen’s point is simply “what has the API been doing to my content?”

Boundaries worth knowing

  • A job is not a build. Contents submitted through the jobs API write records; the static site still rebuilds on its usual triggers. After a large block/content job, dispatch a build (from the dashboard or the same API) as part of the same automation step.
  • No scheduling. Jobs run immediately after submission — there is no “delay until later” semantics; scheduled publishing lives in the content layer (see Scheduling), not in this queue.
  • Types are fixed. The three listed above are the only job types; bulk updates of arbitrary subsets and other entity kinds have their own one-shot bulk endpoints instead of job type slots.
  • Size isn’t capped server-side. A job can carry any number of items (batch tools like bulk_import slice themselves into hundreds to stay polite); the queue simply runs down the list.
  • One special failure: if the worker can’t connect to the database, the job is explicitly marked failed rather than hanging in “queued” — the only kind of failure that reaches that status midway.

The short version

Big batch = a job: one request (the API key with write), a pair of endpoints to check progress, three kinds of content it can bulk-create/upsert, per-item error reporting, an admin screen with the last 200 rows — and remember it writes rows, not sites: rebuild when your batch settles.