export-events
Exports every event of the database as plain CloudEvents, one per line, exactly as import-events takes them.
- Route:
QUERY /api/v2/management/export-events - Category:
management - Authentication: required, with the API token
- Answer: a stream of NDJSON, without envelopes
An export is the mirror of an import: what it writes, an import takes without conversion, and keeps the ID and the time of every event, so that you can move the events into another database, or remove personal data from them on the way, as GDPR Compliance describes. Unlike read-events, it sends each event without an envelope and without its hashes and its signature, which an import computes anew, and it always sends every event, since an import takes the IDs from 0 on without gaps, so that only a full export imports. To read some of the events, use read-events. An export holds no schemas: read-event-types lists them, to register them in the database you import into.
An export holds the events written up to its start. Writes go on while it runs, and what they add is not part of it.
Request#
The route takes no body and no options. To export every event into a file:
curl \
-X QUERY \
-H "authorization: Bearer secret" \
http://localhost:3000/api/v2/management/export-events \
-o events.ndjson
Answer#
The answer has the content type application/x-ndjson, and holds one event per line, in the order in which the events were written, with these fields, in this order:
| Field | Value |
|---|---|
specversion |
The spec version of the event, 1.0. |
id |
The ID of the event. |
source |
The source of the event. |
subject |
The subject of the event. |
type |
The type of the event. |
time |
The time of the event. |
datacontenttype |
The content type of the data of the event, application/json. |
data |
The data of the event. |
traceparent |
The traceparent of the event, where it has one; otherwise, the field is left out. |
tracestate |
The tracestate of the event, where it has one; otherwise, the field is left out. |
Every field is written as the database holds it. A database without events is answered with an empty body, which an import takes as well.
For a database with two events, the request above is answered with:
{"specversion":"1.0","id":"0","source":"https://library.eventsourcingdb.io","subject":"/books/42","type":"io.eventsourcingdb.library.book-acquired","time":"2026-11-05T09:00:00Z","datacontenttype":"application/json","data":{"title":"2001 – A Space Odyssey","author":"Arthur C. Clarke"}}
{"specversion":"1.0","id":"1","source":"https://library.eventsourcingdb.io","subject":"/books/42","type":"io.eventsourcingdb.library.book-borrowed","time":"2026-11-05T09:00:00Z","datacontenttype":"application/json","data":{"borrowedBy":"/readers/23"},"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}
Unlike every other stream of the API, an export has no end, error, or heartbeat items, since every line is an event that an import takes. So it tells whether it is complete through the connection alone:
- An error after the first event cuts the connection. The answer then breaks off rather than ending cleanly: over HTTP/1.1, the client sees the transfer end before its last chunk, as
curlreports withtransfer closed with outstanding read data remaining, and over HTTP/2, the stream is reset. The server logs the cause. - A long pause can meet the idle timeout of a proxy. An export sends no heartbeats, so where the database takes long for the next events, as for a large event that is read from a slow disk, a proxy between the client and the database may close the connection for being idle, and the answer breaks off as well. Raise the idle timeout of such a proxy for this route, or export where no proxy stands in between.
Checking That an Export Is Complete#
A client that keeps an export should check that it got all of it:
- Treat an answer that breaks off as failed, rather than keeping what arrived: a client library reports an error for a connection that is cut, and
curlexits with an error code other than0. - Compare the export with the database. The IDs of an export begin at
0and follow each other without gaps, so the number of its lines is the ID of its last line plus one. Read the latest event before the export, for example withread-eventson the subject/with the optionsrecursiveset totrueandorderset toantichronological, taking only its first event: the export holds at least that event, and every one before it. - Import into an empty database as a last check: an import refuses IDs with gaps and lines that are cut within their JSON.
Events That Do Not Import Unchanged#
An export writes every event as the database holds it, and an import checks every event as a write checks it today. A database that took events before today's checks, from an earlier release or from a backup made by hand, can hold events that an import refuses, such as a source that is no URI, a subject beyond today's bounds, a specversion other than 1.0, a datacontenttype other than application/json, or a tracestate without a traceparent, which W3C Trace Context does not allow, and which a restore took nonetheless. The export of such a database does not import unchanged: the import reports the first such event, and the line has to be changed before it imports.
Errors#
An export carries no error items. An error before the first event is answered with status 200 and a single error envelope, with the content type application/json, rather than with the stream:
restore-in-progress: A restore or an import is running, which the export does not wait for at its start. Once it has begun, an export waits for a restore instead.store-failed: The store refuses reads after a restore or an import that could not empty it again.file-unreadable: A file of the store that the export reads is damaged, such as an events file whose checksum no longer matches.
For example, while a restore runs:
{
"type": "error",
"data": {
"code": "https://docs.eventsourcingdb.io/errors/restore-in-progress",
"description": "restore is in progress: try again once it has finished",
"details": {
"operation": "read"
}
}
}
An error after the first event, such as a damaged file or the server shutting down, cuts the connection, as described above.
Status Codes#
Before the first event, the server answers with a status code other than 200 where it cannot take the request at all, as described in Errors:
401: The API token is missing or wrong.429: Too many requests were sent, or the client holds too many open streams; the answer says when to try again. An export counts as a stream, as creating a backup does.500: The server failed unexpectedly before the first event. After it, the connection is cut instead.503: The server is shutting down, or began to shut down before the first event.