# Observing Events

This guide shows how to **observe events** in EventSourcingDB using its HTTP API. You'll learn how to **subscribe to live events** as well as how to **replay historic events** if needed.

## Observing Events for a Single Subject

Just like **[reading events](/learn/tutorials/first-steps-with-eventsourcingdb/reading-events)**, **observing events** for a single subject is the simplest possible approach.

Apart from using the **`/api/v1/observe-events`** endpoint instead of `/api/v1/read-events`, the request is conceptually the same:

```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/observe-events
```

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

```
HTTP/1.1 200 OK
```

As with reading, the response body initially contains all previously stored events in **NDJSON** format – but the difference is what happens next: While reading ends after all events have been returned, observing keeps the connection open.

To prevent intermediate proxies or other network components from closing the connection due to inactivity, periodic **heartbeat entries** are sent:

```json
{ "type": "heartbeat", "payload": {}}
```

Whenever a new event is written via another connection, it will be streamed through this endpoint as well.

This allows clients to **subscribe** to a subject and get notified about new events as soon as they happen.

## Observing Events for Multiple or All Subjects

To observe events for **multiple subjects** at once, you can – just like when reading – choose an appropriate base subject and set the **`recursive`** parameter to `true`.

The following request shows how to observe *all* events stored in the database. In addition to the `recursive` flag, it is important to specify the **root subject `/`**:

```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/observe-events
```

## Resuming After Connection Loss

If a client gets disconnected from the database, it can **reconnect** at any time. It will first receive all existing events again, followed by live updates.

If the client **remembers the ID** of the last event it received, it can resume from that exact point by setting the **`lowerBound` parameter**. For example, if the last received event had ID `2010`, the client can use the following request to proceed from the event following the given one:

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

This ensures that no events are lost even if the connection is temporarily unavailable.

## Observing From the Last Event of a Given Type

Also similar to reading, you can start observing from the last occurrence of a specific event type using the **`fromLatestEvent` parameter**. For example, to observe all events that happen after a book was last borrowed, use this command:

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

The **`ifEventIsMissing` parameter** controls what should happen if no matching event is found:

- If set to **`read-everything`**, all events for the given subject will be read.
- If set to **`wait-for-event`**, the request will wait until a matching event occurs, and then begin observing.

This also means that if you specify a **non-existent subject**, you won't get an error. Instead, the behavior depends on the value of the `ifEventIsMissing` parameter.

> **Incompatible Parameters**
>
> You cannot combine `fromLatestEvent` with `lowerBound`.

## Your Turn

Now that you know how to **[write](/learn/tutorials/first-steps-with-eventsourcingdb/writing-events)**, **[read](/learn/tutorials/first-steps-with-eventsourcingdb/reading-events)**, and **observe** events, try combining them – write events and attach one or more observers at the same time.

Here are some ideas:

- Observe all events for a book that does not exist yet, then create the book.
- Observe all events for all readers.
- Observe all events across all books and readers, disconnect in between, then reconnect using the ID of the last received event to resume.
