# 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, hand over the `Revisioned` option to `Query`, with a view that implements `Revisioned`, whose projection is tracked (see **[Tracking Revisions](/docs/architecturekit/v2.0/reading-your-own-writes#tracking-revisions)**), and how long to wait at most:

```go
httpapi.Query(api, mux, "QUERY /api/books", toListBooks, answerBooks(listBooks(catalog)),
  httpapi.Revisioned(catalog, httpapi.DefaultWait),
)
```

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 "Content-Type: application/json" \
  -H "Wait-For-Revision: 1" \
  -d '{}'
```

The route waits until the view has reached this revision, but at most for the given duration, which is five seconds for `httpapi.DefaultWait`. 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`. If the view can not read its revision once it has waited, the request is answered with the error, as with any other error, for example with `503 Service Unavailable` for an error of the category `ErrTransient` (see **[Mapping Errors to Status Codes](/docs/architecturekit/v2.0/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 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` accepts only `QUERY`, 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 the answer of a query without `Revisioned`, or of one 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 query that was asked, with every field. So two callers get the same `ETag` only if their queries are equal: 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 query is built before the route 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, which is why the answer sees neither the request nor the user. Three things get past it:

- The clock, or anything else outside the events. See **[Depending on More Than the Read Model](/docs/architecturekit/v2.0/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.
- The context. A value that a middleware puts into the context, such as the user, 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/v2.0/reading-your-own-writes-over-http#waiting-for-a-revision-without-a-tag)**).

A query that holds a function, a channel, or an unsafe pointer, or a value that contains itself, can not be written into an `ETag`, and its answer goes without one.

*Note that the constants `httpapi.HeaderWaitFor` and `httpapi.HeaderRevision` contain the names of the two headers.*

*Note that `Revisioned` panics for a `nil` view or a negative wait, and so does giving it twice, or along with `Awaiting`. A `nil` pointer counts as a `nil` view, such as a view that was declared but never created with `NewInMemoryView`.*

## Depending on More Than the Read Model

If an answer depends on more than the view, for example on the current date, the simplest way is to 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:

```go
type ListOverdueBooks struct {
  Today string
}

func listOverdueBooks(catalog architecturekit.View[BookItem]) func(context.Context, ListOverdueBooks) ([]BookItem, error) {
  return func(ctx context.Context, q ListOverdueBooks) ([]BookItem, error) {
    return query.Collect(query.Where(catalog.All(ctx), func(item BookItem) bool {
      return item.IsBorrowed && item.BorrowedUntil < q.Today
    }))
  }
}

httpapi.Query(api, mux, "QUERY /api/overdue-books",
  func(r *http.Request, _ httpapi.NoBody, user User) (ListOverdueBooks, error) {
    return ListOverdueBooks{Today: time.Now().Format(time.DateOnly)}, nil
  },
  answerBooks(listOverdueBooks(catalog)),
  httpapi.Revisioned(catalog, httpapi.DefaultWait),
)
```

If the answer takes such a value from elsewhere instead, for example because it reads the clock itself, hand over the `Varying` option as well, with a function of the type `httpapi.Volatile`. It receives the request and returns a value that changes whenever the answer would, and that becomes part of the `ETag` as well:

```go
func today(*http.Request) string {
  return time.Now().Format(time.DateOnly)
}

httpapi.Query(api, mux, "QUERY /api/books-due-today", toListBooksDueToday, answerBooks(listBooksDueToday(catalog)),
  httpapi.Revisioned(catalog, httpapi.DefaultWait),
  httpapi.Varying(today),
)
```

Here, `listBooksDueToday` answers like `listOverdueBooks`, but takes the current day from `time.Now` itself, rather than from its query, and `toListBooksDueToday` has the request type `httpapi.NoBody` and returns that query.

*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 `time.Now()` itself, differs on every request, and so does every `ETag`, so the answer is never `304 Not Modified`.*

*Note that `Varying` panics for `nil`, and so does giving it twice, or without `Revisioned`.*

## 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, hand over the `Awaiting` option instead of `Revisioned`. 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`. As with `Revisioned`, the query is built before the route 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:

```go
type PreviewLoan struct {
  BookID string
}

type loanPreviewBody struct {
  IsAvailable   bool   `json:"isAvailable"`
  BorrowedUntil string `json:"borrowedUntil"`
}

func previewLoan(catalog architecturekit.KeyedView[string, BookItem], loanPeriod time.Duration) func(context.Context, PreviewLoan) (loanPreviewBody, error) {
  return func(ctx context.Context, q PreviewLoan) (loanPreviewBody, error) {
    book, isFound, err := catalog.Get(ctx, q.BookID)
    if err != nil {
      return loanPreviewBody{}, err
    }
    if !isFound {
      return loanPreviewBody{}, fmt.Errorf("%w: book %s", httpapi.ErrNotFound, q.BookID)
    }

    return loanPreviewBody{
      IsAvailable:   !book.IsBorrowed,
      BorrowedUntil: time.Now().Add(loanPeriod).Format(time.DateOnly),
    }, nil
  }
}

httpapi.Query(api, mux, "QUERY /api/books/{id}/loan-preview", toPreviewLoan, previewLoan(catalog, loanPeriod),
  httpapi.Awaiting(catalog, httpapi.DefaultWait),
)
```

Here, `toPreviewLoan` has the request type `httpapi.NoBody`, and takes the ID of the book from the path.

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.

*Note that `Awaiting` panics for a `nil` view or a negative wait, and so does giving it twice, or along with `Revisioned`. A `nil` pointer counts as a `nil` view, such as a view that was declared but never created with `NewInMemoryView`. Since only a query with `Revisioned` has a tag, `Varying` panics along with `Awaiting` as well.*

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

To read its own writes in a handler of your own, for example one that answers in another format than JSON, call the `Await` function with the request, the view, and how long to wait at most. It waits for the revision the request asks for, within the context of the request. Running out of time is not an error, and neither is the end of the context of the request, for example because the caller went away: in both cases, `Await` stops waiting and returns `nil`, so the handler answers with what the view holds. It returns an error if the header holds something that is not a revision, which wraps `httpapi.ErrMalformed` as well as `architecturekit.ErrNotARevision`, or if waiting fails for another reason. Determine the caller first, so that nobody can make the server wait without being allowed to ask:

```go
mux.HandleFunc("GET /api/books.csv", func(w http.ResponseWriter, r *http.Request) {
  if _, err := httpapi.UserOf(r, api); err != nil {
    httpapi.RespondError(w, r, api, err)
    return
  }

  if err := httpapi.Await(r, catalog, httpapi.DefaultWait); err != nil {
    httpapi.RespondError(w, r, api, err)
    return
  }

  // ...
})
```

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