---
title: "Pays"
description: "L'index Meilisearch countries n'a aucune table MySQL : il EST la source de vérité. Hydrogen expose donc un CRUD documents pour piloter directement cet inde…"
---

## Gestion de l'index pays (`/admin/countries` — écriture)

L'index Meilisearch `countries` **n'a aucune table MySQL** : il EST la source de vérité. Hydrogen expose donc un **CRUD documents** pour piloter directement cet index — réservé admin (`ADMIN_API_TOKEN`), **JSON plat brut**.

**Clé primaire** : code ISO 3166-1 alpha-2, normalisé en **MAJUSCULES** (`FR`, `US`) côté index.

**Asynchronisme** : toute écriture Meili (`addDocuments` / `deleteDocument`) renvoie une *task* `enqueued` — le document n'est consultable qu'une fois la task traitée. La réponse remonte le `taskUid` pour suivi. Réponse commune `202` :

```json
{ "status": "enqueued", "id": "FR", "taskUid": 1465508 }
```

**Forme du document** : le corps est le document pays dans la forme **native** de l'index (`name`, `slug`, `codes`, `stats`, `region`, `status`, `continent`, `timezones`, `continent_id`, `official_name`, `_geo`…). La forme **aplatie** de lecture est aussi acceptée : `latitude`/`longitude` sont repliés dans `_geo {lat,lng}` (sauf si `_geo` est fourni explicitement), et les clés dérivées en lecture (`distanceMeters`, `description`, `_geoDistance`, `_formatted`, `_rankingScore*`, `_matchesPosition`) sont ignorées. La PK `id` est **toujours** forcée depuis le code validé (route ou corps) — un `id` divergent dans le corps est écrasé.

## `POST /admin/countries`

**Création** d'un pays. `id` (alpha-2) et `name` requis dans le corps.

| Status | Body | Sens |
|---|---|---|
| `202` | `{ status, id, taskUid }` | écriture enqueued |
| `400` | `{ "error": "Request body must be a JSON object." }` | JSON invalide |
| `409` | `{ "error": "Country already exists." }` | la PK existe déjà → utiliser `PUT` |
| `422` | `{ "error": "Field 'id' must be a 2-letter ISO code." }` / `"Field 'name' is required."` | validation |
| `503` | `{ "error": "<message Meili>" }` | backend Meili injoignable |

```bash
curl -s -X POST -H "Authorization: Bearer $ADMIN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"id":"FR","name":"France","slug":"france","continent":"Europe","latitude":46.2,"longitude":2.2}' \
  "http://hydrogen.dev.com/admin/countries"
```

## `PUT /admin/countries/{code}`

**Remplacement total** d'un pays existant. La PK vient de la route (`{code}`, 2 lettres). `name` requis (remplacement complet, pas de merge partiel).

| Status | Body | Sens |
|---|---|---|
| `202` | `{ status, id, taskUid }` | écriture enqueued |
| `400` | `{ "error": "Request body must be a JSON object." }` | JSON invalide |
| `404` | `{ "error": "Country not found." }` | la PK n'existe pas → utiliser `POST` |
| `422` | `{ "error": "Field 'name' is required." }` | validation |
| `503` | `{ "error": "<message Meili>" }` | backend Meili injoignable |

```bash
curl -s -X PUT -H "Authorization: Bearer $ADMIN_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"France","slug":"france","continent":"Europe"}' \
  "http://hydrogen.dev.com/admin/countries/fr"
```

## `DELETE /admin/countries/{code}`

**Suppression** d'un pays. `404` si absent (pas de suppression idempotente silencieuse).

| Status | Body | Sens |
|---|---|---|
| `202` | `{ status, id, taskUid }` | suppression enqueued |
| `404` | `{ "error": "Country not found." }` | la PK n'existe pas |
| `503` | `{ "error": "<message Meili>" }` | backend Meili injoignable |

```bash
curl -s -X DELETE -H "Authorization: Bearer $ADMIN_API_TOKEN" \
  "http://hydrogen.dev.com/admin/countries/fr"
```

---

## `GET /admin/social-feeds/{code}`

Détail d'un social feed en **JSON:API 1.1** (cf. [Convention de format](#convention-de-format--détails-360-en-jsonapi)) : **strict superset** du public `GET /api/social-feeds/{code}` — même enveloppe (`type: "socialFeeds"`, `id` = code Crockford Base32) et même forme d'attributs (via `SocialFeedResourceSerializer`). Deux différences admin :

- Le carrousel n'est **pas filtré aux médias publiés** : l'admin voit **tous** les médias du feed (y compris ceux dépubliés après coup et masqués au partage public).
- Chaque média embarqué est rendu **avec le propriétaire du feed comme viewer**, ce qui fait remonter le bloc de modération owner-only (`flag` / `isRejected`).

`404` (erreur JSON:API) sur code malformé ou feed inconnu.

```bash
curl -s -H "Authorization: Bearer $ADMIN_API_TOKEN" \
  -H "Accept: application/vnd.api+json" \
  "http://hydrogen.dev.com/admin/social-feeds/A1B2C3"
```

---
