File Upload & Processing

Accept files through forms and APIs, store them as assets, and process them safely in the background.

Uploads come in two flavors: a user dropping a file into your page, and a system pushing a file to your API. Both end up as cluster assets, and heavy processing belongs in the queue. This tutorial covers the whole pipeline.

Upload via a Page (User-Facing)

  1. Build a form with state_form and a file-capable input from the state elements — see the form pattern in Block Templates.
  2. On submit, read the form with state_form_get; the file arrives with its name, type, and size.
  3. Store it via the asset upload API (POST /api/v1/_system/assets/stream) or a file-handling block, into public (world-readable) or system_assets (private) — choose per public vs private.
  4. Record the asset guid/path on a document so you can find it later (e.g. photo on a profile document).

Upload via an API (Machine-Facing)

  1. Register on_startup_register_uri_post with your upload URI — see the upload template.
  2. Validate: content type, size limit, required metadata. Reject with a 400 before storing anything.
  3. Store as an asset; reply 200 with {"guid": "...", "path": "..."} so the caller can reference it.
  4. Scope it deliberately — uploads are never *public by accident; use *loggedin or a custom scope with a secret in the path.

Processing: Do It in the Background

Resizing, parsing, importing, converting — never inline in the request. The pattern:

  1. The upload flow stores the file and enqueues a processing job with function_custom_execute_from_queue — see Queue.
  2. A queue worker flow picks it up: reads the file with file_from_assets, transforms it (e.g. resize/convert with the image blocks), writes results back to a document or a new asset.
  3. Mark progress on the job document (status: queued → processing → done|failed) so users can poll or a page can show state.
  4. Log failures to the console and storage — the error-pins pattern from Block Execution.

Safety Rules