# Reading Events

This guide shows how to **read events** from EventSourcingDB using its **HTTP API**. You'll learn how to read either all or a desired **subset of the previously stored events**.

## Reading From a Single Subject

Once you've **[written events](/learn/tutorials/first-steps-with-eventsourcingdb/writing-events)**, the next step is typically to read them back. The simplest approach is to read the events for a single **subject**. All you need is the subject name, e.g. **`/books/42`**.

To read events, send an HTTP request using **`POST`** to the `/api/v1/read-events` endpoint.

> **Why POST for Reading?**
>
> You might wonder why `POST` is used for reading instead of `GET`. The main reason is that `POST` allows parameters to be passed in the request body, which is much more convenient than encoding everything in the query string – especially when parameters become complex.
>
> For this reason, almost all API routes of EventSourcingDB use `POST`, regardless of whether they perform actions or retrieve data.

To read all events for the subject `/books/42`, run the following command:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/books/42\",
    \"options\": {
      \"recursive\": false
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

If everything worked as expected, the server will respond with **HTTP status code `200 OK`**:

```
HTTP/1.1 200 OK
```

The response body will then contain a list of all stored events for that subject. If you've followed the steps in **[Writing Events](/learn/tutorials/first-steps-with-eventsourcingdb/writing-events)**, you should see the following two events:

```json
{
  "type": "event",
  "payload": {
    "specversion": "1.0",
    "id": "0",
    "time": "...",
    "source": "https://library.eventsourcingdb.io",
    "subject": "/books/42",
    "type": "io.eventsourcingdb.library.book-acquired",
    "datacontenttype": "application/json",
    "data": {
      "author": "Arthur C. Clarke",
      "isbn": "978-0756906788",
      "title": "2001 – A Space Odyssey"
    },
    "predecessorhash": "...",
    "hash": "...",
    "signature": null
  }
}
{
  "type": "event",
  "payload": {
    "specversion": "1.0",
    "id": "1",
    "time": "...",
    "source": "https://library.eventsourcingdb.io",
    "subject": "/books/42",
    "type": "io.eventsourcingdb.library.book-borrowed",
    "datacontenttype": "application/json",
    "data": {
      "borrowedBy": "/readers/23",
      "borrowedUntil": "..."
    },
    "predecessorhash": "...",
    "hash": "...",
    "signature": null
  }
}
```

In reality, the output will look slightly different – the events are returned as compact **NDJSON**, with each event occupying a single line.

> **Why NDJSON Instead of JSON?**
>
> Note that the response is formatted as NDJSON, which is *not* valid JSON.
>
> According to the JSON standard, only one top-level element is allowed. If multiple items need to be returned – as is the case here – they would have to be wrapped in an array. However, this would be impractical for stream-based processing, since a client would need to buffer the entire array before it could begin parsing.
>
> That's why EventSourcingDB uses the NDJSON format whenever multiple items need to be returned: each item appears on its own line, separated by newline characters. This allows for **efficient streaming** and **incremental parsing**.

If you specify a non-existent subject, you won't get an error. Instead, you will get an HTTP status code `200 OK` as well, but with an empty response body.

## Reading From Multiple Subjects

Suppose you want to read not just the events for a single book, but all events for all books – in their original (interleaved) order. To do that, specify the **parent subject `/books`**.

However, just changing the subject alone won't work, because no events have been written to `/books` itself.

To include the sub-subjects (e.g., `/books/42`), you must explicitly set the **`recursive`** option to **`true`**:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/books\",
    \"options\": {
      \"recursive\": true
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

### Reading All Events

You can use the same mechanism to read all events stored in EventSourcingDB, in chronological order. Simply use the **root subject `/`** and ensure to set **`recursive`** to **`true`**, because all subjects form a hierarchy starting from `/`:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/\",
    \"options\": {
      \"recursive\": true
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

> **Lots of Events**
>
> Before doing this in a production environment, keep in mind that it may result in a large number of events being read.
>
> From a database perspective, this is not a problem – EventSourcingDB is optimized for reading large event streams. But you may generate a significant amount of network traffic. Use this option responsibly.

## Fine-Tuning Reads

In addition to the required `recursive` parameter, there are several optional parameters you can use to control how events are read.

### Reading in Reverse Order

You can control the order in which events are read using the **`order` parameter**. The default is `chronological`. However, sometimes you may want to read in reverse – starting with the most recent events. This can be useful e.g. when displaying a history view where the most recent changes should be shown first.

To do this, set `order` to **`antichronological`**:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/books/42\",
    \"options\": {
      \"recursive\": false,
      \"order\": \"antichronological\"
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

### Reading a Specific Range

By default, all events for the given subject are read. But sometimes you may want to restrict the range – for example, when implementing pagination.

You can use the **`lowerBound`** and **`upperBound`** parameters (individually or together) to limit the result. Both bounds can be configured to be **`inclusive`** or **`exclusive`**:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/books/42\",
    \"options\": {
      \"recursive\": false,
      \"lowerBound\": {
        \"id\": \"2001\",
        \"type\": \"inclusive\"
      },
      \"upperBound\": {
        \"id\": \"3001\",
        \"type\": \"exclusive\"
      }
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

> **IDs Are Strings**
>
> Even if IDs look numeric, you must pass them as strings. This is required by the **[CloudEvents](/docs/eventsourcingdb/cloud-events)** specification, which defines event IDs to be strings.

### Reading From the Last Event of a Given Type

Sometimes it's useful to read all events that occurred after a certain type of event – for example, everything that happened since a book was last borrowed.

You can achieve this using the **`fromLatestEvent`** option (which starts reading from the most recent event of a given type):

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  -H "content-type: application/json" \
  -d "{
    \"subject\": \"/books/42\",
    \"options\": {
      \"recursive\": false,
      \"fromLatestEvent\": {
        \"subject\": \"/books/42\",
        \"type\": \"io.eventsourcingdb.library.book-borrowed\",
        \"ifEventIsMissing\": \"read-everything\"
      }
    }
  }" \
  http://localhost:3000/api/v1/read-events
```

Reading from the latest event is especially useful for performance optimization when combined with **[Snapshots](/docs/eventsourcingdb/snapshots)**.

A key question here is: what happens if no matching event of that type is found? That behavior is controlled using the **`ifEventIsMissing`** option:

- If set to **`read-everything`**, all events for the given subject will be read.
- If set to **`read-nothing`**, the response will be empty.

> **Incompatible Parameters**
>
> You **cannot combine `fromLatestEvent` with `lowerBound`**.
>
> Also, reading from the latest event of a given type is **only supported in chronological order** – not antichronological.

## Your Turn

Try writing some events about books (such as `book acquired` and `book borrowed`) and readers (such as `reader applied` and `reader accepted`). Then read these events in different ways.

Here are some ideas:

- Read all events for a specific book in **anti-chronological order**.
- Read all events **for all books and all readers** using a single request.
- Read all events for a specific book **since it was last borrowed**.

You can also extend the model – for example, by introducing another sub-subject for book pages, such as `/books/42/pages/23`, to record events like a page being damaged.

Then try the following:

- Read all events for a book including its pages.
- Read all events for a specific book and its pages since the book was last borrowed.
