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=estimated
```

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: true # opt in to Prefer: count=exact scans (off by default; otherwise served as estimates)
```

<VersionBadge version="v5.3.0" /> `exactCount` (default `false`) 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, it is off by default; set this to `true` to enable exact counts for this application's REST interface. A `count=exact` request is otherwise served as a cheaper estimate.

## URL Structure

The REST interface follows a consistent URL structure:
Expand Down
56 changes: 56 additions & 0 deletions reference/rest/querying.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,62 @@ 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 applies only to `GET`/`HEAD` requests and requires a `limit()` within a supported page size — a request with no `limit()`, an oversized one, or a non-numeric one is served normally with no count headers, since counting an unbounded page 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 opt-in per mount (see below) and served as an estimate unless enabled. |
| `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.

### Enabling exact counts

Because an exact count scans the full matched set, it is **off by default**. Enable it in an application's REST configuration via the [`exactCount` option](./overview.md#configuration):

```yaml
rest:
exactCount: true
```

Without it, a `count=exact` request is served as an estimate (the response reports `Preference-Applied: count=estimated`). Estimated counts are always available.

<VersionBadge version="v4.3.0" />

## Relationships and Joins
Expand Down