# Exposer un CPT en lecture seule via l’API REST sans installer d’extension

> Pas besoin d'un plugin supplémentaire pour rendre un type de contenu personnalisé accessible en lecture depuis un front headless. Trois arguments suffisent, correctement réglés.

- Auteur : Clément Hadrot
- Publié le : 2020-04-09
- Mis à jour le : 2020-04-09
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/cpt-lecture-seule-api-rest-sans-extension/

## L’essentiel

- show_in_rest suffit pour l'essentiel
- rest_base évite les collisions d'URL
- Un contexte de lecture seule se garde sans permission_callback complexe

Un client nous a récemment demandé d'ajouter un type de contenu « Témoignages » à son site headless, sans rien écrire côté React. Sa première réaction a été de chercher un plugin pour « exposer ça en API ». Il n'y en avait pas besoin. WordPress sait exposer un type de contenu personnalisé via l'API REST nativement depuis longtemps, à condition de déclarer le bon type avec les bons arguments.

Cette recette couvre le cas le plus fréquent d'un front headless simple : un CPT lu uniquement en front public, jamais modifié depuis le site, sans champ personnalisé complexe à gérer pour l'instant. Les champs ACF et les mutations en écriture sont volontairement laissés de côté, ils méritent un traitement à part.

## Le problème : un CPT invisible pour l'API REST par défaut

Par défaut, `register_post_type()` ne rend pas un type de contenu accessible à l'API REST, même s'il apparaît normalement dans l'administration. C'est un choix délibéré de WordPress : rien n'est exposé publiquement sans déclaration explicite. Un développeur pressé ajoute parfois une extension entière juste pour ce comportement, alors que trois arguments du même `register_post_type()` suffisent.

## Le snippet commenté

```
add_action( 'init', function () {
    register_post_type( 'temoignage', array(
        'labels'       => array(
            'name'          => 'Témoignages',
            'singular_name' => 'Témoignage',
        ),
        'public'       => true,
        'has_archive'  => false,
        'supports'     => array( 'title', 'editor', 'thumbnail' ),

        // Les trois réglages qui comptent pour l'API REST :
        'show_in_rest'  => true,               // active l'exposition REST
        'rest_base'     => 'temoignages',       // URL: /wp-json/wp/v2/temoignages
        'rest_controller_class' => 'WP_REST_Posts_Controller', // valeur par défaut, explicite ici
    ) );
} );
```

> L'essentiel à retenir : show_in_rest suffit pour l'essentiel ; rest_base évite les collisions d'URL ; Un contexte de lecture seule se garde sans permission_callback complexe

Avec ces réglages, le point de terminaison `GET /wp-json/wp/v2/temoignages` devient immédiatement disponible, avec pagination, filtres et champ `context` hérités du contrôleur natif, sans une ligne de code supplémentaire côté API.

## Verrouiller l'accès en lecture seule

Le contrôleur par défaut de WordPress gère déjà les méthodes d'écriture (`POST`, `PUT`, `DELETE`) en vérifiant les permissions de l'utilisateur connecté. Sans compte authentifié, ces méthodes échouent naturellement avec une erreur 401. Pour un front headless anonyme, ce comportement par défaut correspond exactement au besoin : lecture publique, écriture réservée aux comptes autorisés dans l'administration. Aucun `permission_callback` personnalisé n'est nécessaire dans ce cas précis.

Si l'on veut être plus strict encore et empêcher toute tentative d'écriture même par un compte mal configuré, on peut désactiver l'édition côté REST sans toucher à l'administration :

```
add_filter( 'rest_prepare_temoignage', function ( $response ) {
    // Purement défensif : on ne modifie rien ici,
    // le filtre sert de point d'ancrage pour un futur contrôle.
    return $response;
} );
```

## Vérifier ce qui est réellement exposé

Une erreur fréquente consiste à supposer que `show_in_rest` suffit sans vérifier le résultat concret. La commande WP-CLI suivante liste tous les types de contenu et indique lesquels sont exposés :

```
wp post-type list --fields=name,show_in_rest,rest_base
```

Elle permet de repérer en une ligne un CPT oublié, ou un `rest_base` qui entre en collision avec un autre point de terminaison déjà utilisé par un plugin tiers.

## Les pièges à éviter

- Oublier `rest_base` : WordPress utilisera par défaut le nom du type de contenu, parfois moins lisible côté front (`temoignage` au singulier plutôt que `temoignages`).
- Déclarer le CPT après le hook `rest_api_init` : le crochet `init` reste le bon choix, il s'exécute avant l'initialisation de l'API REST.
- Confondre `public` et `show_in_rest` : un CPT public dans l'administration n'est pas automatiquement exposé côté API, ce sont deux réglages indépendants.

## Variantes utiles

Pour restreindre les champs renvoyés sans installer de plugin, il est possible de filtrer directement la réponse avec `rest_prepare_{post_type}`, en retirant du tableau `$response->data` les clés non désirées avant de le retourner. C'est une alternative légère à `register_rest_field` quand il s'agit seulement de masquer des champs natifs plutôt que d'en ajouter de nouveaux.

Autre variante fréquente : limiter le nombre de résultats par défaut avec l'argument `rest_default_per_page` disponible depuis les versions récentes de l'API, pour éviter qu'un front mal codé ne récupère l'intégralité des témoignages en un seul appel.

## En résumé

Trois arguments de `register_post_type()`, un hook `init`, et un point de terminaison REST en lecture seule est prêt, sans dépendance externe. Cette approche minimale convient parfaitement à un front headless qui se contente de lire du contenu : elle évite d'ajouter une extension de plus à maintenir, pour un besoin que le cœur de WordPress couvre déjà très bien.
