◂ Retour à [README](./README.md)

# Query & Count (read entities)

Pour la syntaxe des `filters`, `orders` et `selects`, voir [query-language.md](./query-language.md).

## Query (read entities)

```
POST {api}/{shardId}/{entity}/query
```

**Request body:**

```json
{
  "page": { "index": 0, "size": 50 },
  "filters": { },
  "orders": [ ],
  "selects": { }
}
```

- `page` — **required**. `index` starts at 0, `size` between 1 and 300 (recommended: 50).
- `filters` — optional. A predicate expression (see "Query Language" section below).
- `orders` — optional. An array of order clauses (see "Ordering" below). If omitted, the API sorts by `id` when available.
- `selects` — optional. A projection expression (see "Projection" below). Defaults to `{ "$all": true }`.

**Response:** `{ "data": [ ...entities ] }`

### Pagination

The API uses **stateless offset-based pagination** (no cursor). To fetch all records, increment `page.index` until `data.length < page.size`. Because pagination is stateless, **duplicates can occur across pages** — always deduplicate results by `id`. Missing records can also happen if the sort property is non-unique or mutable; omitting `orders` lets the API pick the safest sort.

### Selecting specific fields

Return only specific properties:

```json
{
  "selects": { "$all": false, "mail": true, "firstname": true },
  "page": { "index": 0, "size": 50 }
}
```

### Expanding related entities (joins)

```json
{
  "selects": {
    "$all": true,
    "speakers": { "$all": true }
  },
  "page": { "index": 0, "size": 50 }
}
```

Related entities are **never loaded automatically** — you must explicitly request them in `selects`.

---

## Count

```
POST {api}/{shardId}/{entity}/count
```

Same body as query (supports `filters` and `page`) but without `selects`. Returns a **single integer**.
