# Filtres, tri et pagination

Une collection peut contenir des milliers d'éléments. Le client doit pouvoir n'en demander qu'une partie, dans l'ordre qui l'intéresse. Tout passe par les **paramètres de query** : la ressource reste la même, on ne change que la vue.

### Les filtres

Un paramètre par critère, avec le nom du champ :

```text
GET /v1/cocktails?alcohol_free=true
GET /v1/cocktails?tag=classic&max_price=10
GET /v1/orders?status=pending
```

<p class="callout warning">Pas de filtre dans le chemin : `/cocktails/alcohol-free` laisse croire qu'il s'agit d'une ressource à part, et ne se combine pas avec les autres filtres.</p>

### Le tri

Un paramètre `sort` avec le nom du champ. Un `-` devant inverse l'ordre, et on peut trier sur plusieurs champs séparés par des virgules :

```text
GET /v1/cocktails?sort=price          prix croissant
GET /v1/cocktails?sort=-price         prix décroissant
GET /v1/cocktails?sort=-price,name    prix décroissant, puis nom
```

<p class="callout info">Un tri par défaut doit toujours exister (souvent par `id` ou par date) : sans lui, l'ordre peut changer d'une page à l'autre et des éléments apparaissent en double.</p>

### La pagination

#### Par numéro de page (offset)

```text
GET /v1/cocktails?page=2&per_page=5
```

- ✅ Simple, on peut sauter directement à la page 5.
- ❌ Si des éléments sont ajoutés pendant qu'on parcourt, on voit des doublons ou on en rate.
- ❌ Lent sur de très grosses tables (la base doit compter les lignes à sauter).

#### Par curseur

```text
GET /v1/orders?limit=20
GET /v1/orders?limit=20&cursor=eyJpZCI6NDJ9
```

Le serveur renvoie un **curseur** opaque qui marque la position atteinte. C'est le choix de Stripe, Slack ou Twitter.

- ✅ Stable même si des données arrivent, et rapide.
- ❌ Impossible de sauter à une page précise.

#### Ce que la réponse doit contenir

Le client doit savoir où il en est et comment continuer. Deux approches, souvent combinées :

- des métadonnées et des liens dans le body ;
- le header `Link` (RFC 8288), utilisé par exemple par GitHub.

```json
{
  "items": [ ... ],
  "page": 2,
  "per_page": 5,
  "total": 13,
  "_links": {
    "self":  {"href": "/v1/cocktails?page=2&per_page=5"},
    "first": {"href": "/v1/cocktails?page=1&per_page=5"},
    "prev":  {"href": "/v1/cocktails?page=1&per_page=5"},
    "next":  {"href": "/v1/cocktails?page=3&per_page=5"},
    "last":  {"href": "/v1/cocktails?page=3&per_page=5"}
  }
}
```

```http
Link: </v1/cocktails?page=3&per_page=5>; rel="next", </v1/cocktails?page=3&per_page=5>; rel="last"
```

<p class="callout warning">Fixez toujours une **limite maximale** à `per_page`. Sinon, quelqu'un finira par demander `?per_page=1000000`, et votre serveur s'en souviendra.</p>

### En résumé

<table id="bkmrk-besoinconvention-fil"><thead><tr><th>Besoin</th><th>Convention</th></tr></thead><tbody><tr><td>Filtrer</td><td>`?champ=valeur`</td></tr><tr><td>Trier</td><td>`?sort=-champ1,champ2`</td></tr><tr><td>Paginer</td><td>`?page=&per_page=` ou `?cursor=&limit=`</td></tr><tr><td>Naviguer</td><td>`_links` et/ou header `Link`</td></tr></tbody></table>