Data ops

The export is a snapshot, not the exit

You can export a view to a CSV whenever you like — filtered, streamed by a worker, downloaded from a link only you can open. But it's a convenience, not the escape hatch, because the escape hatch is that the data was already in a Postgres database you own.

SchemaStack team26 Aug 2026Verified working · 26 Aug 2026Docs

Most tools treat export as the emergency door — the thing you reach for when you want your data back. In SchemaStack it isn't, and that's the whole point: your data has been in a standard Postgres database you control the entire time. The export button is a convenience for handing someone a file, not the way out. There is no way out to build, because you were never locked in.

So what the export is turns out to be more interesting than what it replaces.

Your viewfilter applied202Job queuedRabbitMQWorkerstreams → CSVFilein S3Downloadowner only · expires in 24hCSV or JSON — nospreadsheet format,despite the docs.
Export is an async job, not a response: the view's current filter is serialized, a worker streams the rows to a CSV, and the file lands in object storage behind a link only its owner can open — for 24 hours, then it is deleted.

It's a job, not a response

Ask for an export and you don't wait for a download — you get a 202 and a job. The view's current filter is serialized and handed to a worker, which opens a cursor on your database, streams the rows, and writes a CSV. When it's done, the file is in object storage and a link appears in your Downloads tray. Nothing blocks while a large table is read.

That the worker runs the export, rather than the API, is deliberate: the API is on a platform with a hard response-size ceiling, and an export can be larger than that. Moving the file-serving to a worker with no such limit is why a big export downloads at all.

Only your rows, only your file

Two things narrow what comes out. The filter on your view travels with the export when you export a selection — "all rows matching these conditions" becomes the WHERE clause the worker runs, so you export what you're looking at, not the whole table. And the download is yours alone: the link checks that the person fetching it is the person who ran the export. A colleague with the link can't open it; the file simply isn't theirs.

Then it expires. Twenty-four hours after it's written, the file is gone and the link returns a plain "expired" rather than someone else's stale data. If you need it again, you run it again — which is cheap, because it's a snapshot of a live table, not a thing you have to keep.

What it doesn't do (yet)

  • No Excel. Every export is a CSV; there is no spreadsheet writer, so no native .xlsx workbook. Open the CSV in Excel, which reads it fine. The docs used to advertise an Excel option that did not exist — that page has since been corrected rather than left to be discovered after clicking.
  • The toolbar "Export" ignores your active filter. Exporting from the selection bar carries the filter; the plain toolbar button exports the whole table. Two buttons, two behaviours — worth knowing which one you pressed.
  • Formula columns don't survive the export. A computed column has no physical column behind it, so a view that includes one can't be exported as-is. This contradicts an older changelog note; the honest position is that exporting a formula column doesn't work today.
  • Headers are database column names. The export uses the real column names, not the display names you see in the grid, and hidden columns are included rather than dropped.
  • Sort order isn't preserved. Rows come out ordered by primary key for a deterministic file, not in the order your view is sorted.
  • The whole file is built in memory before it's stored. The read from your database is streamed, but the file is assembled in one buffer, so a very large export is bounded by the worker's memory and a sixty-second query timeout rather than by a stated row limit.
  • The download is per-person, so a workspace admin can't fetch a teammate's export. The check is identity equality against whoever ran it.
  • No scheduled or whole-workspace export. Every export is a manual, one-view, one-shot job. There is no cron, and no "export everything."

The bulk operations guide covers the mapping in detail — and if you find yourself scripting exports to move data around, the REST API and your own database are the durable paths; the file is just the quick one.

Verified 26 Aug 2026: 27 tests, all green 2026-08-26 — BulkActionServiceTest in processor-test (the export builder against a real Postgres container, covering selected-ids and filtered exports, CSV and JSON, IN/LIKE filters, UUID and composite primary keys) and BulkActionHandlerTest in metadata-test (the job lifecycle — COMPLETED persists the download path and expiry, FAILED persists neither). The CSV-only and 24-hour-expiry limits were read straight from ExportFormat and the storage services; the download endpoint's own ownership and expiry checks have no automated test, and this post says so. The bulk-operations docs page was corrected the same day to drop the Excel format it advertised but never had..