CSV import / export
Bulk-load records into a base collection from a CSV, or export every row as CSV — one click in the admin or one HTTP call.
For schema portability use migration snapshots. For data portability — moving records between environments, importing seed data, exporting a snapshot to a spreadsheet — use CSV.
Available on base collections only (auth and view collections aren’t exposed). Open the Records page for a collection and look at the toolbar.
Export
Section titled “Export”GET /api/v1/admin/export/<collection> ← admin auth → text/csv response, served as a downloadOr click Export on the Records page. The downloaded file:
- Includes every column except
passwordfields (always stripped — they’re Argon2 hashes, never exportable). - JSON-encodes object/array values (e.g. multi-select
["a","b"], file fields withmultiple: true,geoPoint,jsonfields) into a single cell — round-trippable on import. - Starts with the system columns
id,createdandupdated(Unix seconds), then the collection’s fields in schema order.
id,created,updated,title,body,published,tagsabc123,1730000000,1730000123,Hello,World,true,"[""a"",""b""]"Excel / Google Sheets / xsv / pandas.read_csv all open this without
issue. CSV escaping follows RFC 4180 (double-quote everything containing
commas, quotes, or newlines; double-up quote chars inside).
Import
Section titled “Import”POST /api/v1/admin/import/<collection> ← admin authContent-Type: text/csv
<csv body> → { "data": { "created": 42, "failed": 1, "total": 43, "errors": [ { "row": 7, "details": { "title": "..." } } ] } }row is the line in the file, counting the header as row 1. errors lists
at most 50 rows.
Or use Import on the Records page: pick a file and it uploads straight away. A toast shows how many rows were imported; failed rows are logged to the browser console.
What happens per row
Section titled “What happens per row”- Parse the CSV row into a record-shaped object using the header line.
- Run it through the standard
validateRecord()pipeline — same validation rules as the records API. - Create a new record with a fresh id. Import only creates records; it never updates existing ones.
- On error, push
{ row: <line>, details: <message> }intoerrorsand continue with the next row.
Rows are inserted one by one, not in one transaction: a failing row is
reported in errors and the rows around it still import.
Headers Vaultbase recognizes
Section titled “Headers Vaultbase recognizes”| Header | Effect |
|---|---|
id, created, updated | Ignored. Every row gets a new id and fresh timestamps. |
| Any field name | Mapped to the field by exact match. |
| Unknown column | Ignored. |
For multi-value or JSON columns, supply the JSON-encoded form just like export produces:
id,tags,metadata,"[""a"",""b""]","{""color"":""blue""}"Common errors
Section titled “Common errors”{ "row": 3, "details": { "title": "title must be at least 3 characters" } }{ "row": 7, "details": { "email": "email must be unique" } }Validation errors are the same per-field details object the records API
returns on 422; other errors are a plain message string. Fix the rows in
the spreadsheet and re-upload only the failed ones.
Limits
Section titled “Limits”- No row limit — but very large imports lock the SQLite writer; chunk to ~1k rows per request if you’re loading hundreds of thousands.
- Export is streamed: the response body is a
ReadableStreamthat pages throughlistRecordsin batches of 500. Memory stays bounded regardless of collection size; client disconnects stop paging viacancel(). - Import is buffered:
POST /api/v1/admin/import/:collectionparses the whole file into memory before processing. Chunk huge imports manually if you’re loading hundreds of thousands of rows. - Files aren’t included: file-type fields hold the filename only; the actual bytes need to be uploaded separately via the Files API.
Round-trip workflow
Section titled “Round-trip workflow”A common use case — export from prod, edit in a spreadsheet, re-import:
# 1. Exportcurl -H "Authorization: Bearer $ADMIN" \ http://prod.example.com/api/v1/admin/export/posts > posts.csv
# 2. Edit posts.csv in Excel / Sheets / vim
# 3. Re-import (always creates new records with new ids)curl -X POST -H "Authorization: Bearer $ADMIN" \ -H "Content-Type: text/csv" \ --data-binary @posts.csv \ http://staging.example.com/api/v1/admin/import/postsImport can’t update existing records: re-importing an export creates a
copy of every row, and rows that hit a unique field fail. To change
existing records in bulk, use the Batch API with PATCH
operations.
See also
Section titled “See also”- Backups & migrations — schema-only snapshots.
- Batch API — atomic multi-op transactions for programmatic bulk writes.