# Handling Queries over HTTP

A query is asked with the method `QUERY`, which HTTP defines in RFC 10008. Like `GET`, it is safe, so it changes nothing, but like `POST`, it carries a body, so a query takes its input from the body, as a command does. To answer a query over HTTP, define a request type with JSON annotations for the body, and a function that returns the query.

Like the function that returns a command, it receives the request, the body, decoded into the request type, and the user:

```go
type listBooksRequest struct {
  OnlyAvailable bool `json:"onlyAvailable"`
  Limit         int  `json:"limit"`
}

func toListBooks(r *http.Request, request listBooksRequest, user User) (ListBooks, error) {
  return ListBooks{
    OnlyAvailable: request.OnlyAvailable,
    Limit:         request.Limit,
  }, nil
}
```

As for a command, the request type describes the body, and the query is what the application works with, so the query needs no JSON annotations. The function builds the query from the request type, so it may check the input, and add what the body does not hold, such as the user or a value of the path, as `toBorrowBook` adds the reader to the command (see **[Handling Commands over HTTP](/docs/architecturekit/v2.0/handling-commands-over-http)**).

Items carry no JSON annotations (see **[Defining Views](/docs/architecturekit/v2.0/defining-views)**). So to answer with JSON, define a response type with JSON annotations, as the counterpart of the request types that commands use (see **[Handling Commands over HTTP](/docs/architecturekit/v2.0/handling-commands-over-http)**), and map the items to it. Here, the revision of a book goes along as `eventId`, so that a caller can send it back as `expectedEventId`:

```go
type bookBody struct {
  ID            string `json:"id"`
  Title         string `json:"title"`
  Author        string `json:"author"`
  IsBorrowed    bool   `json:"isBorrowed"`
  BorrowedUntil string `json:"borrowedUntil"`
  EventID       string `json:"eventId"`
}

func bookBodyOf(book BookItem) bookBody {
  return bookBody{
    ID:            book.ID,
    Title:         book.Title,
    Author:        book.Author,
    IsBorrowed:    book.IsBorrowed,
    BorrowedUntil: book.BorrowedUntil,
    EventID:       book.EventID,
  }
}
```

To answer with such bodies, wrap the function that answers the query with items, such as `listBooks` (see **[Defining Queries](/docs/architecturekit/v2.0/defining-queries)**), and map what it returns. Since every query that lists books returns items, one wrapper serves all of them:

```go
func answerBooks[TQuery any](ask httpapi.Answer[TQuery, []BookItem]) httpapi.Answer[TQuery, []bookBody] {
  return func(ctx context.Context, q TQuery) ([]bookBody, error) {
    books, err := ask(ctx, q)
    if err != nil {
      return nil, err
    }

    bodies := make([]bookBody, 0, len(books))
    for _, book := range books {
      bodies = append(bodies, bookBodyOf(book))
    }

    return bodies, nil
  }
}
```

Then call the `Query` function with the API, the mux, a pattern, the function that returns the query, and the function that answers it:

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

The pattern names the method `QUERY`. A pattern without a method, which accepts every method, or with another method, makes `Query` panic. `net/http` does not name the method yet, so the constant `httpapi.MethodQuery` does, as `http.MethodGet` names `GET`, for example for a request in a test. A handler of your own that answers with `Ask` may use another method, though, for example `GET` for a download or a CSV export (see **[Answering Queries in Your Own Format](/docs/architecturekit/v2.0/handling-queries-over-http#answering-queries-in-your-own-format)**).

The caller sends the input as JSON, as for a command:

```shell
curl -X QUERY http://localhost:8080/api/books \
  -H "Content-Type: application/json" \
  -d '{"onlyAvailable":true}'
```

The route determines the user, and then decodes the body by the same rules as for a command (see **[Validating Requests](/docs/architecturekit/v2.0/handling-commands-over-http#validating-requests)**), before it calls the function that returns the query. So a body that does not claim to be JSON, that is too large, or that is not valid JSON or does not fit the request type is answered with `415`, `413`, or `400`, and the same message as for a command.

A query without input, such as one that takes everything it needs from the path, has the request type `httpapi.NoBody`, so that the caller sends no body, or `{}`. As for a command without a body, a request that a browser sends from another origin is then answered with `403 Forbidden` (see **[Handling Commands Without a Body](/docs/architecturekit/v2.0/handling-commands-over-http#handling-commands-without-a-body)**):

```go
func toGetBook(r *http.Request, _ httpapi.NoBody, user User) (GetBook, error) {
  return GetBook{BookID: r.PathValue("id")}, nil
}
```

The route answers with `200 OK` and the result as JSON, with `Cache-Control: no-store`, so that no cache keeps it (see **[Reading Your Own Writes over HTTP](/docs/architecturekit/v2.0/reading-your-own-writes-over-http)**). The result is encoded as `encoding/json` encodes it, except that a `nil` slice is answered as an empty list, `[]`, and a `nil` map as an empty object, `{}`, at every depth, such as the `nil` slice that `query.Collect` returns when there are no items, or a field of a response type that holds a `nil` slice. So a caller gets a list or an object, whether it holds anything or not. A `nil` pointer, on the other hand, is answered as `null`. A result that can not be encoded, for example because it holds `NaN`, is a mistake in the code, and is answered with `500 Internal Server Error` and logged, like any other internal failure. Errors and panics are answered as for commands, and errors returned from the first function are treated as they are from the function that returns a command (see **[Authorizing Commands](/docs/architecturekit/v2.0/handling-commands-over-http#authorizing-commands)**).

To answer this way in a handler of your own, call the `RespondResult` function with the response writer, the request, the API, the result, and the error. As with `Respond`, an error without a status code of its own is answered with `500 Internal Server Error`, so wrap a mistake in the request that the handler finds itself with `httpapi.ErrMalformed` (see **[Handling Commands over HTTP](/docs/architecturekit/v2.0/handling-commands-over-http)**). Like the route, it answers a result that can not be encoded with `500 Internal Server Error`, also if a `MarshalJSON` function panics while it is encoded, and logs the panic with its value and its stack, rather than leaving `net/http` to close the connection without an answer. To answer an error without a result, call the `RespondError` function (see **[Answering Queries in Your Own Format](/docs/architecturekit/v2.0/handling-queries-over-http#answering-queries-in-your-own-format)**).

*Note that a `nil` slice of bytes is answered as an empty string, `""`, since a slice of bytes is encoded as a string in base64, and that a `nil` `json.RawMessage` is answered as `null`, since it holds JSON text.*

*Note that the functions have the types `httpapi.ToQuery` and `httpapi.Answer`. The answering function receives neither the request nor the user.*

*Note that calling `Query` with `nil` as the API, for either function, or as one of the options, panics, rather than failing every request.*

## Answering Queries in Your Own Format

To answer in a format of your own, call the `Ask` function in a handler of your own. It does the same as a route, but writes nothing to the response. Instead, it returns the result. Such a handler may use another method than `QUERY`, for example `GET` for a CSV export that a browser can download from a link. A `GET` request carries no body, so its function has the request type `httpapi.NoBody`, and takes the input from the URL instead:

```go
toExportBooks := func(r *http.Request, _ httpapi.NoBody, user User) (ListBooks, error) {
  return ListBooks{
    OnlyAvailable: r.URL.Query().Get("available") == "true",
  }, nil
}

mux.HandleFunc("GET /api/books.csv", func(w http.ResponseWriter, r *http.Request) {
  books, err := httpapi.Ask(r, api, toExportBooks, listBooks(catalog))
  if err != nil {
    httpapi.RespondError(w, r, api, err)
    return
  }

  w.Header().Set("Content-Type", "text/csv")
  // ...
})
```

To answer an error the way a route does, call the `RespondError` function with the response writer, the request, the API, and the error, as above. It answers with the same status codes and messages as `RespondResult`, says `Cache-Control: no-store`, and logs the same way, but needs no result. As with `RespondResult`, an error without a status code of its own is answered with `500 Internal Server Error`.

*Note that a `GET` request without a body passes from any origin, unlike a `QUERY` request without one, since `GET` must not change anything (see **[Handling Commands Without a Body](/docs/architecturekit/v2.0/handling-commands-over-http#handling-commands-without-a-body)**).*

*Note that calling `RespondError` with `nil` as the API, or with a `nil` error, panics.*

*Note that `Ask` returns a panic as an error, as `Handle` does (see **[Answering Commands in Your Own Format](/docs/architecturekit/v2.0/handling-commands-over-http#answering-commands-in-your-own-format)**).*

*Note that calling `Ask` with `nil` as the API, or for either function, panics, as with `Query`, but on every request, even one whose caller is unknown. That panic, too, comes back as an error, which names the mistake.*

## Asking for Several Items at Once

Since a `QUERY` request carries a body, the input of a query may hold more than a URL could, for example a list of books to check at once. Here, the answer tells for every book whether it is available:

```go
type CheckAvailability struct {
  BookIDs []string
}

func checkAvailability(catalog architecturekit.KeyedView[string, BookItem]) func(context.Context, CheckAvailability) (map[string]bool, error) {
  return func(ctx context.Context, q CheckAvailability) (map[string]bool, error) {
    isAvailable := make(map[string]bool, len(q.BookIDs))
    for _, bookID := range q.BookIDs {
      book, isFound, err := catalog.Get(ctx, bookID)
      if err != nil {
        return nil, err
      }

      isAvailable[bookID] = isFound && !book.IsBorrowed
    }

    return isAvailable, nil
  }
}

type checkAvailabilityRequest struct {
  BookIDs []string `json:"bookIds"`
}

func toCheckAvailability(r *http.Request, request checkAvailabilityRequest, user User) (CheckAvailability, error) {
  return CheckAvailability{BookIDs: request.BookIDs}, nil
}

httpapi.Query(api, mux, "QUERY /api/check-availability", toCheckAvailability, checkAvailability(catalog))
```

## Reporting Missing Items

If the answering function returns `query.ErrNoItems`, as `query.Single` does if no item matches, the request is answered with `404 Not Found`, and the message `not found`, since the text of `query.ErrNoItems`, `no items`, does not say what was not found. The error is logged at level `Info`, as for `401` and `409`. So a wrapper that maps a single item hands on the error as it is:

```go
func answerBook[TQuery any](ask httpapi.Answer[TQuery, BookItem]) httpapi.Answer[TQuery, bookBody] {
  return func(ctx context.Context, q TQuery) (bookBody, error) {
    book, err := ask(ctx, q)
    if err != nil {
      return bookBody{}, err
    }

    return bookBodyOf(book), nil
  }
}

httpapi.Query(api, mux, "QUERY /api/books/{id}", toGetBook, answerBook(getBook(catalog)))
```

Here, `toGetBook` is the function of a query without input, whose request type is `httpapi.NoBody` (see above).

To report a missing item yourself, return `httpapi.ErrNotFound`, or an error that wraps it, such as `fmt.Errorf("%w: book %s", httpapi.ErrNotFound, q.BookID)`. Such an error is written for the caller, so it is the message, here `not found: book 42`.
