# Reading Your Own Writes over HTTP

A caller that sends a query right after a command can have the query wait until the view has caught up with the write of the command.

To let a caller read its own writes over HTTP, take a `Reading` in the handler, an extractor that reads the headers with which the caller waits and asks. Build the query, and then let the reading answer with its `revisioned` method. It takes a view that implements `Revisioned`, whose projection records every event it has seen (see **[Tracking Revisions](/docs/architecturekit/rust/reading-your-own-writes#tracking-revisions)**), the query, how long to wait at most, and a function that answers the query:

```rust
use architecturekit::httpapi::{DEFAULT_WAIT, Reading};

pub async fn handle_list_books(
  State(app): State<App>,
  _: User,
  reading: Reading,
  Strict(request): Strict<ListBooksRequest>,
) -> Result<QueryAnswer<Vec<BookBody>>, HttpError> {
  let query = ListBooks { only_available: request.only_available, limit: request.limit };

  reading
    .revisioned(&app.catalog_projection.catalog, &query, DEFAULT_WAIT, || async {
      let books = list_books(&app.catalog_projection.catalog, query.clone()).await?;
      Ok(QueryAnswer::new(books.into_iter().map(BookBody::from).collect()))
    })
    .await
}
```

The query goes into the tag of the answer (see below), and the function that answers hands `list_books` a clone of it, so its type has to derive `Serialize` and `Clone`, here `ListBooks`. After sending a command, the caller takes the revision from the answer and sends it in the `Wait-For-Revision` header of the query:

```shell
curl -X QUERY http://localhost:8080/api/books \
  -H "Authorization: Bearer …" \
  -H "Content-Type: application/json" \
  -H "Wait-For-Revision: 1" \
  -d '{}'
```

**The reading waits until the view has reached this revision, but at most for the given duration**, which is five seconds for `DEFAULT_WAIT`. Then it answers with what the view holds, even if the time has run out. Without the header, it does not wait at all. If the header holds something that is not a revision, the request is answered with `400 Bad Request`, as in `malformed request: not a revision: "abc"`. If the view fails while the reading waits, or can not read its revision once it has waited, the request is answered with the error, by its category, for example with `503 Service Unavailable` for an error of the category `Category::Transient` (see **[Mapping Errors to Status Codes](/docs/architecturekit/rust/mapping-errors-to-status-codes)**).

Once the view has seen at least one event, the response contains the revision it shows in the `Revision` header, as well as an `ETag` header and `Cache-Control: private, no-cache`. `no-cache` makes a cache ask again before it hands out the answer, and `private` keeps shared caches, such as proxies, from keeping it at all. If the caller sends the `ETag` in the `If-None-Match` header, asks the same, and the view has not changed since, the request is answered with `304 Not Modified`, which carries the same headers, and no body. The function that answers is not called then.

The header is read as HTTP has it: it may hold a list of tags, separated by commas, or `*`, which stands for any tag. A tag also counts if it is marked as weak, as `W/"…"`, which a proxy does when it compresses the answer.

HTTP has `304 Not Modified` for `GET` and `HEAD` requests, and for `QUERY`, which it treats like `GET`, also when the query asks with a body. Since `query` routes only `QUERY` to the handler, a query whose answer has not changed is always answered with `304 Not Modified`. Browsers, though, never cache answers to `QUERY`, so they never send `If-None-Match` for one. The `ETag` and `304 Not Modified` only help a client that keeps the `ETag` and sends it itself, such as one of your own.

Every other answer that the kit writes carries no revision, and says `Cache-Control: no-store`, so that no cache keeps it. That holds for the answer to a command, for every failure, and for a `QueryAnswer` that a handler returns without `revisioned`, or whose view has not seen any event yet. Without a word on caching, HTTP would let a cache keep such an answer for a while it picks itself, and hand it out again.

**The `ETag` holds the path and the query string of the request, and the query that was asked, with every field.** So two callers get the same `ETag` only if they ask the same address with equal queries: a query that holds the user, or anything else that tells callers apart, gets an `ETag` of its own for each of them. That matters as soon as callers share a client that keeps the tags, one after the other, since it asks with the `ETag` it kept for the one before. The handler builds the query before the reading waits or tells the caller that nothing has changed, so a caller who may not ask is refused first.

This holds as long as the answer depends on nothing but the query and the view. The function that answers is a closure, which could capture anything, so keep it to the query and the view. Three things get past the tag:

- The clock, or anything else outside the events. See **[Depending on More Than the Read Model](/docs/architecturekit/rust/reading-your-own-writes-over-http#depending-on-more-than-the-read-model)**.
- Another view. The revision is that of the view handed over, so an answer that also reads from another view does not notice when that one changes.
- Anything else the closure captures, such as the user. It never shows up in the `ETag`. Put it into the query instead.

For an answer that depends on more than the query and `varying` can capture, wait without a tag (see **[Waiting for a Revision Without a Tag](/docs/architecturekit/rust/reading-your-own-writes-over-http#waiting-for-a-revision-without-a-tag)**).

A query that serde can not encode as JSON, such as one with a map whose keys are tuples, can not be written into an `ETag`, and its answer goes without one.

*Note that the constants `WAIT_FOR_REVISION` and `REVISION` contain the names of the two headers.*

*Note that for a web framework other than axum, `Reading::of` takes the same from the parts of a request of the `http` crate.*

## Depending on More Than the Read Model

If an answer depends on more than the view, for example on the current date, put that value into the query, as `today` below. Since the query is part of the `ETag`, the `ETag` changes with it, and `revisioned` is all it takes:

```rust
use chrono::Local;

#[derive(Serialize)]
pub struct ListOverdueBooks {
  pub today: String,
}

pub async fn list_overdue_books<V: View<BookItem>>(
  catalog: &V,
  query: &ListOverdueBooks,
) -> Result<Vec<BookItem>, V::Error> {
  catalog
    .all()
    .try_filter(|book| future::ready(book.is_borrowed && book.borrowed_until < query.today))
    .try_collect()
    .await
}

pub async fn handle_list_overdue_books(
  State(app): State<App>,
  _: User,
  reading: Reading,
  _: NoBody,
) -> Result<QueryAnswer<Vec<BookBody>>, HttpError> {
  let query = ListOverdueBooks { today: Local::now().format("%Y-%m-%d").to_string() };

  reading
    .revisioned(&app.catalog_projection.catalog, &query, DEFAULT_WAIT, || async {
      let books = list_overdue_books(&app.catalog_projection.catalog, &query).await?;
      Ok(QueryAnswer::new(books.into_iter().map(BookBody::from).collect()))
    })
    .await
}
```

If the answer takes such a value from elsewhere instead, for example because it reads the clock itself, call the `varying` method on the reading as well, with a value that changes whenever the answer would. It becomes part of the `ETag` as well:

```rust
pub async fn handle_list_books_due_today(
  State(app): State<App>,
  _: User,
  reading: Reading,
  _: NoBody,
) -> Result<QueryAnswer<Vec<BookBody>>, HttpError> {
  let query = ListBooksDueToday;

  reading
    .varying(Local::now().format("%Y-%m-%d"))
    .revisioned(&app.catalog_projection.catalog, &query, DEFAULT_WAIT, || async {
      let books = list_books_due_today(&app.catalog_projection.catalog, &query).await?;
      Ok(QueryAnswer::new(books.into_iter().map(BookBody::from).collect()))
    })
    .await
}
```

Here, `list_books_due_today` answers like `list_overdue_books`, but takes the current day from the clock itself, rather than from its query, and `ListBooksDueToday` is a struct without fields that derives `Serialize`.

*Note that such a value, whether it is part of the query or comes from `varying`, holds the time only as precisely as the answer depends on it, such as the day or the hour, never as an instant. An instant, such as `Local::now()` itself, differs on every request, and so does every `ETag`, so the answer is never `304 Not Modified`.*

*Note that `varying` matters only for `revisioned`, since `awaiting` tags nothing (see below).*

## Waiting for a Revision Without a Tag

Some answers depend on more than the query and `varying` can capture, for example on the instant, on the configuration, or on another view. A tag would then tell a caller that nothing has changed when it has. To let the caller read its own writes all the same, call the `awaiting` method of the reading instead of `revisioned`, and answer afterwards. **It waits exactly as `revisioned` does, but tags nothing**: the answer carries neither a `Revision` header nor an `ETag`, says `Cache-Control: no-store`, as every answer without a revision does, and is never `304 Not Modified`. Its extractors take the request apart before the handler waits, so a body that does not fit is refused at once.

For example, a preview of borrowing a book tells whether the book is available, which the caller wants to see right after returning it, and until when the book would be borrowed, which depends on the current time and on the loan period, which comes from the configuration. So the state of the router holds the loan period as well, as a `TimeDelta` of the chrono crate:

```rust
use chrono::{TimeDelta, Utc};

#[derive(Clone)]
pub struct App {
  // ...
  pub loan_period: TimeDelta,
}
```

The handler waits for the revision the caller asks for, and then answers with the book and the loan period:

```rust
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoanPreviewBody {
  is_available: bool,
  borrowed_until: String,
}

pub async fn handle_preview_loan(
  State(app): State<App>,
  Path(book_id): Path<String>,
  _: User,
  reading: Reading,
  _: NoBody,
) -> Result<QueryAnswer<LoanPreviewBody>, HttpError> {
  reading.awaiting(&app.catalog_projection.catalog, DEFAULT_WAIT).await?;

  let Some(book) = app.catalog_projection.catalog.get(&book_id).await? else {
    return Err(HttpError::not_found(format!("book {book_id}")));
  };

  Ok(QueryAnswer::new(LoanPreviewBody {
    is_available: !book.is_borrowed,
    borrowed_until: (Utc::now() + app.loan_period).format("%Y-%m-%d").to_string(),
  }))
}
```

So use `revisioned` while the answer depends on nothing but the query and the view, plus what `varying` adds, since the `ETag` then saves sending an answer that has not changed. Use `awaiting` for an answer that depends on more, and neither for a query whose caller never needs to read its own writes.

## Waiting for a Revision in a Handler of Your Own

To read its own writes in a handler that answers in another format than JSON, such as the CSV export (see **[Answering Queries in Your Own Format](/docs/architecturekit/rust/handling-queries-over-http#answering-queries-in-your-own-format)**), call `awaiting` as well. It waits for the revision the request asks for. **Running out of time is not an error**: `awaiting` then stops waiting and returns `Ok`, so the handler answers with what the view holds. It returns an error if the header holds something that is not a revision, which is answered with `400 Bad Request`, or if the view fails while it waits. Determine the caller first, so that nobody can make the server wait without being allowed to ask, which the extractor of the user does before the handler runs:

```rust
pub async fn handle_export_books(
  State(app): State<App>,
  _: User,
  reading: Reading,
  Query(parameters): Query<ExportBooksParameters>,
) -> Result<impl IntoResponse, HttpError> {
  reading.awaiting(&app.catalog_projection.catalog, DEFAULT_WAIT).await?;

  // ...
}
```

*Note that such a handler answers with neither an `ETag` nor `304 Not Modified`. Those come with `revisioned`, which ties the `ETag` to the query, so that callers never share one by accident. For a query that answers in the default format, `awaiting` waits the same way (see **[Waiting for a Revision Without a Tag](/docs/architecturekit/rust/reading-your-own-writes-over-http#waiting-for-a-revision-without-a-tag)**).*
