Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions reference/rest/headers.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ These headers are included in all Harper REST API responses:
| `etag` | `"abc123"` | Encoded version/last-modification time of the returned record. Used for conditional requests. |
| `location` | `/MyTable/new-id` | Returned on `POST` responses. Contains the path to the newly created record. |

Collection responses to a [count request](./querying.md#pagination-and-total-count) additionally include `Content-Range`, `Range-Unit`, and `Preference-Applied` (<VersionBadge version="v5.3.0" />).

## Request Headers

### Content-Type
Expand Down Expand Up @@ -67,6 +69,19 @@ Accept-Encoding: gzip, br

Compression is particularly effective for JSON responses. For binary formats like CBOR, compression provides diminishing returns compared to the already-compact encoding.

### Prefer

<VersionBadge version="v5.3.0" />

Opt in to a total match count on a collection `GET`/`HEAD`, returned via the `Content-Range` response header for pagination:

```http
GET /Product/?category=software&limit(0,25)
Prefer: count=exact
```

Accepts `count=exact` (precise, scans the full matched set) or `count=estimated` (fast, approximate). See [Pagination and Total Count](./querying.md#pagination-and-total-count) for the full request/response contract.

### Authorization

Credentials for authenticating requests. See [Security Overview](../security/overview.md) for details on supported authentication mechanisms (Basic, JWT, mTLS).
Expand Down
3 changes: 3 additions & 0 deletions reference/rest/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ rest: true
rest:
lastModified: true # enables Last-Modified response header support
webSocket: false # disables automatic WebSocket support (enabled by default)
exactCount: false # serve Prefer: count=exact requests as estimates instead of scanning
```

<VersionBadge version="v5.3.0" /> `exactCount` (default `true`) controls whether the [pagination total-count](./querying.md#pagination-and-total-count) feature honors `Prefer: count=exact`. Because an exact count scans the full matched set, set this to `false` to serve those requests as cheaper estimates for this application's REST interface.

## URL Structure

The REST interface follows a consistent URL structure:
Expand Down
49 changes: 49 additions & 0 deletions reference/rest/querying.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,55 @@ GET /Product/?rating=gt=3&sort(+name)
GET /Product/?sort(+rating,-price)
```

## Pagination and Total Count

<VersionBadge version="v5.3.0" />

Use `limit(start,end)` to page through a collection, and opt in to a total match count with the `Prefer` request header (`Prefer: count=exact`) so a client can render pagination (for example "1-25 of 1,234") without a second request.

Counting is opt-in: without the header, no count is computed and no count headers are returned. It also requires a `limit()` — a count request on an unbounded collection (no `limit()`) is served normally, with no count headers, since counting the whole collection would defeat the point of paging.

### Requesting a count

Send a `Prefer` header on a `GET` (or `HEAD`) request to a collection:

| Value | Meaning |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| `count=exact` | The exact number of matching records. Scans the full matched set, so it is more expensive than the page itself. |
| `count=estimated` | A fast planner/table estimate. Cheap, approximate. |

```http
GET /Product/?category=software&limit(0,25)
Prefer: count=exact
```

### Count response headers

The count is returned in [RFC 7233](https://datatracker.ietf.org/doc/html/rfc7233)-style response headers alongside the page body:

| Header | Example | Description |
| -------------------- | ----------------- | --------------------------------------------------------------------------------------------- |
| `Content-Range` | `items 0-24/1234` | The 0-based, inclusive range of records returned (`start-end`) out of the total matching set. |
| `Range-Unit` | `items` | The unit used by `Content-Range`. |
| `Preference-Applied` | `count=exact` | The count mode the server applied (`exact` or `estimated`). |

```http
HTTP/1.1 200 OK
Content-Range: items 0-24/1234
Range-Unit: items
Preference-Applied: count=exact
```

The response status is always `200` — `Content-Range` is informational (Harper does not use `206 Partial Content`). A `HEAD` request with `Prefer: count=` returns the count headers with no body, a cheap way to ask "how many match?" without transferring the page. When CORS is enabled, these three headers are added to `Access-Control-Expose-Headers` so browser clients can read them cross-origin.

### Unavailable totals

The total is reported as `*` (for example `Content-Range: items 0-24/*`) when it cannot be produced — an exact scan that reaches its internal work limit, or a query with no cardinality estimate (for example a `!=` or `=ct=` (contains) condition). `Preference-Applied` still echoes the requested mode, so an unavailable total (`.../*`) is distinct from a request that asked for no count.

### Disabling exact counts

Because an exact count scans the full matched set, you can disable it in an application's REST configuration via the [`exactCount` option](./overview.md#configuration). With `exactCount: false`, a `count=exact` request is served as an estimate instead (the response reports `Preference-Applied: count=estimated`). Estimated counts are always available; the default is `true`.

<VersionBadge version="v4.3.0" />

## Relationships and Joins
Expand Down