---
contentId: 28eecb1d-69ce-435d-b21d-977628bd65ff
title: Intégrer Cloudflare AI Search à Docus et VitePress
description: Découvrez comment j'ai intégré Cloudflare AI Search à Docus et VitePress, synchronisé leur contenu et contribué à la prise en charge d'AI Gateway.
date: 2026-08-24
---

En juillet 2025, [NuxtLabs a rejoint Vercel](https://vercel.com/blog/nuxtlabs-joins-vercel). En tant que grand fan du framework et de l'équipe, j'étais vraiment heureux de les voir rejoindre une entreprise comme Vercel. Le travail open source est extrêmement exigeant et l'équipe consacrait beaucoup d'énergie à construire une base financière durable. Rejoindre Vercel lui donne plus de liberté pour se concentrer sur le framework et son écosystème. Elle fait un travail formidable. _Merci à vous !_

## Faire Entrer Cloudflare dans l'Univers Nuxt

Depuis que NuxtLabs a rejoint Vercel, les services de Vercel sont naturellement devenus plus visibles dans l'écosystème qui l'entoure. Cette préférence ne s'étend pas à Nuxt lui-même, qui reste indépendant des plateformes grâce en grande partie au travail de [Daniel](https://github.com/danielroe) sur le framework. Il en va de même pour les primitives UnJS sous-jacentes, grâce au travail de [Pooya](https://github.com/pi0).

Pour ma part, je suis un utilisateur de Cloudflare. J'utilise beaucoup de ses services pour développeurs depuis des années et, parfois, la documentation ne couvre que Vercel.

Je veux changer cela. Je veux renforcer la présence de Cloudflare dans l'écosystème Nuxt pour en faire davantage un acteur de premier plan. Heureusement, l'équipe Nuxt est ouverte aux contributions et accueille vraiment bien les changements qui rendent son travail compatible avec d'autres plateformes que Vercel. _Honnêtement, il est tout à fait compréhensible qu'elle optimise d'abord pour Vercel. Je suis simplement ce type qui a décidé d'utiliser Cloudflare._

## Docus, le Framework de Documentation pour Nuxt

L'aventure commence avec Docus.

Docus est un framework de documentation construit sur Nuxt Content. Il permet de créer un site de documentation en quelques secondes avec de nombreuses fonctionnalités prêtes à l'emploi. Il inclut un moteur de recherche, un assistant, un serveur MCP, la distribution de skills pour agents et bien d'autres fonctionnalités qui facilitent la création d'un site de documentation. Ah, et il est magnifique.

Pendant longtemps, j'ai utilisé VitePress parce qu'il était beaucoup plus simple. Mais depuis l'essor de l'IA, Docus est devenu bien mieux adapté à des besoins tels que les assistants, l'accès pour les agents et la distribution de skills. J'utilise donc désormais Docus partout où j'ai besoin d'un site de documentation.

Mais si vous consultez la documentation, vous verrez que l'assistant intégré est réservé à Vercel et qu'il n'existe aucune intégration avec Cloudflare AI Search pour remplacer la recherche plein texte intégrée.

### Le Module Nuxt pour Docus

Docus repose sur Nuxt. Cela signifie que nous pouvons créer un module Nuxt pour l'étendre ou en surcharger le fonctionnement.

En réalité, la recherche de Docus est propulsée par un composant nommé `AppSearch`. Nous pouvons modifier son comportement en le remplaçant simplement par notre propre composant. Dans un module Nuxt, il suffit d'enregistrer un composant avec une priorité supérieure à celle du composant intégré pour que notre implémentation prenne le dessus.

```ts
import { addComponent, createResolver, defineNuxtModule } from 'nuxt/kit'

export default defineNuxtModule<AiSearchOptions>({
  setup() {
    const resolver = createResolver(import.meta.url)

    addComponent({
      priority: 100,
      name: 'AppSearch',
      filePath: resolver.resolve('./runtime/components/AppSearch.vue'),
    })
  },
})
```

Nous avons alors un contrôle total sur le composant `AppSearch`. Nous pouvons réutiliser le composant `ContentSearch` de Nuxt UI et lui transmettre les résultats de l'endpoint public de Cloudflare AI Search.

Et ça fonctionne à merveille !

<figure>
  <video autoplay loop muted playsinline>
    <source src="https://images.soubiran.dev/posts/bringing-cloudflare-ai-search-to-docus-and-vitepress/docus.mp4" type="video/mp4">
  </video>
  <figcaption>Intégration de Cloudflare AI Search dans Docus</figcaption>
</figure>

Je suis vraiment satisfait du résultat et je l'utiliserai sans aucun doute dans mes prochains projets. Vous pouvez consulter la démo sur [docus-cloudflare-ai-search.barbapapazes.dev](https://docus-cloudflare-ai-search.barbapapazes.dev) ou le [dépôt GitHub](https://github.com/Barbapapazes/docus-cloudflare-ai-search).

Pour utiliser le module, vous devez l'installer :

```bash
pnpm add docus-cloudflare-ai-search
```

Vous devez ensuite ajouter le module à votre fichier `nuxt.config.ts` :

```ts
import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({
  modules: ['docus-cloudflare-ai-search'],
  docus: {
    aiSearch: {
      endpoint: 'https://<your-domain>',
    },
  },
})
```

En travaillant sur ce module, j'ai réalisé que l'assistant intégré n'était disponible qu'avec Vercel. Dommage, car disposer de Cloudflare AI Search sans pouvoir exécuter l'assistant avec Cloudflare était bien loin de ce que j'appellerais une expérience Cloudflare native.

L'assistant utilise une passerelle d'IA pour acheminer les requêtes vers son modèle. Docus prenait en charge Vercel AI Gateway, mais n'exposait pas la configuration nécessaire pour utiliser Cloudflare AI Gateway à la place. J'ai donc ouvert une PR pour ajouter les options de configuration du fournisseur, de la passerelle et du modèle requises pour Cloudflare : [feat(assistant): can use cloudflare for assistant](https://github.com/nuxt-content/docus/pull/1421). La PR conserve Vercel comme option par défaut et fait de Cloudflare un fournisseur facultatif configuré par des variables d'environnement. _Comme toujours dans l'open source, il suffit d'ouvrir une PR pour concrétiser une idée._

## Il Faut du Contenu pour Effectuer une Recherche

L'intégration fonctionne, ce qui est une belle première étape. Mais pour le moment, notre index de recherche est vide. Cloudflare AI Search propose trois façons de le remplir :

1. Utiliser le stockage intégré, alimenté depuis le tableau de bord ou l'API Items
2. Connecter un bucket R2
3. Explorer un site web public sur un domaine qui vous appartient

Les téléversements manuels depuis le tableau de bord ne conviennent pas, car le contenu doit être synchronisé à chaque déploiement en production. Un bucket R2 est une solution viable, mais téléverser le contenu n'est pas aussi simple que d'utiliser rclone, car chaque fichier a également besoin de métadonnées personnalisées. Cela nécessiterait un outil supplémentaire. L'exploration d'un site web peut extraire des métadonnées personnalisées depuis les balises HTML `<meta>`, mais ajouter ces balises à chaque page générée nécessiterait aussi une intégration sur mesure.

La meilleure solution consiste donc à alimenter le stockage intégré via l'API Items, qui permet d'associer des métadonnées personnalisées lors du téléversement de chaque fichier. Cela nécessite toujours une intégration sur mesure, mais avec la bonne intégration, nous pouvons vraiment simplifier l'expérience des développeurs.

Pour Nuxt, vous pouvez utiliser le package [cloudflare-ai-search-sync](https://github.com/Barbapapazes/cloudflare-ai-search-sync) :

Installez le package :

```bash
pnpm add cloudflare-ai-search-sync
```

Ajoutez ensuite le module à votre fichier `nuxt.config.ts` :

```ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/content',
    'cloudflare-ai-search-sync/nuxt',
  ],
  cloudflareAISearchSync: {
    enabled: true,
  },
})
```

Désormais, chaque fois que vous compilez votre projet Nuxt, les fichiers Markdown traités par Nuxt Content sont téléversés vers Cloudflare AI Search avec les bonnes métadonnées. On dirait de la magie !

Cette intégration n'est pas obligatoire tant que vous fournissez les métadonnées appropriées pour votre contenu.

## Aller Plus Loin

Ajouter une nouvelle fonctionnalité à l'écosystème Nuxt a été très simple grâce au système de modules de Nuxt. Il fournit des points d'extension pour un large éventail de cas d'usage. Il suffit de consulter la page des [modules Nuxt](https://nuxt.com/modules) pour découvrir toutes les possibilités.

Pour essayer une idée en local, les modules sont parfaitement adaptés. Créez un projet Nuxt, ajoutez un dossier `modules` et commencez à bricoler votre idée. Si vous souhaitez publier le module, le starter officiel de modules Nuxt fournit la structure complète du projet. Donnez la documentation des modules Nuxt à votre assistant IA et vous pourrez obtenir un prototype fonctionnel étonnamment vite. Il n'y a jamais eu de meilleur moment pour essayer.

Mais tout l'écosystème Vue ne repose pas sur Nuxt. Nous avons VitePress et une multitude de plugins Vite pour créer nos propres systèmes. Les plugins Vite sont d'ailleurs un bon moyen de toucher un public plus large. Un plugin Vite peut souvent servir des frameworks tels qu'Astro, Svelte, React et même Nuxt, même si chaque intégration peut encore nécessiter un travail spécifique au framework. Cependant, les plugins Vite sont beaucoup plus difficiles à créer. Commencer à un haut niveau avec un module Nuxt, vérifier que l'idée fonctionne vraiment, puis descendre dans la pile pour créer un plugin Vite est donc une bonne façon de procéder.

Et c'est exactement ce que j'ai fait avec l'intégration de Cloudflare AI Search.

### VitePress Était le Premier Choix Évident

Une fois la preuve de concept validée pour Docus, j'ai commencé à réfléchir à la manière de la rendre disponible pour VitePress. J'ai commencé en tant qu'utilisateur de VitePress, mon portfolio l'utilise toujours et le framework dispose d'une vaste communauté autour de la documentation.

VitePress prend également déjà en charge nativement la recherche locale et Algolia, ainsi que plusieurs plugins tiers pour d'autres moteurs de recherche. Intégrer Cloudflare AI Search à VitePress était donc un choix naturel et je pouvais tirer parti du travail déjà réalisé pour l'intégration à Docus.

Grâce au [snippet Cloudflare AI Search](https://search.ai.cloudflare.com/), j'ai pu intégrer rapidement le moteur de recherche à VitePress. Une fois la preuve de concept validée, j'ai créé un plugin VitePress dédié : <GitHubLink repo="barbapapazes/vitepress-plugin-cloudflare-ai-search" />.

Sous le capot, le plugin est assez simple. Il remplace le composant intégré `VPNavBarSearch` par un composant personnalisé qui intègre le snippet de Cloudflare AI Search et reçoit sa configuration via un module virtuel.

Pour commencer, vous devez installer le plugin :

```bash
pnpm add vitepress-plugin-cloudflare-ai-search
```

Vous devez ensuite ajouter le plugin à votre fichier `config.ts` :

```ts
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { cloudflareAISearch } from 'vitepress-plugin-cloudflare-ai-search'

export default defineConfig({
  vite: {
    plugins: [
      cloudflareAISearch({
        endpoint: 'https://<your-domain>',
      }),
    ],
  },
})
```

Vous voudrez peut-être aussi téléverser votre contenu vers le moteur AI Search afin de le rendre consultable. Pour cela, vous pouvez également utiliser le package [cloudflare-ai-search-sync](https://github.com/Barbapapazes/cloudflare-ai-search-sync), qui s'en chargera automatiquement.

Installez le package :

```bash
pnpm add cloudflare-ai-search-sync
```

Ajoutez ensuite le plugin à votre fichier `config.ts` :

```ts
import { cloudflareAISearchSync } from 'cloudflare-ai-search-sync/vitepress'
import { defineConfig } from 'vitepress'

export default defineConfig({
  buildEnd: cloudflareAISearchSync({ enabled: true }),
})
```

<figure>
  <video autoplay loop muted playsinline>
    <source src="https://images.soubiran.dev/posts/bringing-cloudflare-ai-search-to-docus-and-vitepress/vitepress.mp4" type="video/mp4">
  </video>
  <figcaption>Intégration de Cloudflare AI Search dans VitePress</figcaption>
</figure>

Vous pouvez désormais compiler votre projet VitePress et son contenu sera synchronisé avec AI Search. On dirait de la magie !

Si vous préférez simplement l'essayer, vous pouvez consulter la démo sur [vitepress-plugin-cloudflare-ai-search.barbapapazes.dev](https://vitepress-plugin-cloudflare-ai-search.barbapapazes.dev) ou le [dépôt GitHub](https://github.com/Barbapapazes/vitepress-plugin-cloudflare-ai-search).

## Tout ne s'est pas Passé Comme Prévu

Vous pensez peut-être : « Super, ça fonctionne bien ! » Mais ce n'est pas ce qui s'est passé au départ et je l'ai appris à mes dépens. À ce moment-là, la [documentation de Cloudflare](https://developers.cloudflare.com/ai-search/configuration/retrieval/public-endpoint/embed-search-snippets/) expliquait comment intégrer le snippet d'interface, mais elle n'établissait pas clairement le lien entre ce processus et l'indexation du contenu avec les métadonnées attendues par le composant. Sans contenu indexé au format attendu, le snippet d'interface ne fonctionne pas.

Comme mentionné précédemment, il existe trois façons de rendre du contenu accessible au moteur de recherche :

1. Utiliser le stockage intégré via le tableau de bord ou l'API Items
2. Connecter un bucket R2
3. Explorer un site web public

Aucune d'entre elles ne fonctionnait directement avec le snippet. Pire encore, après les avoir ajustées et avoir essayé de modifier les clés ou les métadonnées des éléments, rien ne fonctionnait. J'ai donc fait ce que je fais le mieux : je suis allé consulter le code source de l'[intégration EmDash](https://docs.emdashcms.com/deployment/cloudflare/#cloudflare-ai-search) et du [snippet d'interface](https://github.com/cloudflare/ai-search-snippet). _Heureusement pour moi, il est open source._

J'ai découvert qu'EmDash n'interroge pas directement l'endpoint public. Son intégration expose plutôt [un endpoint dédié qui réécrit les métadonnées](https://github.com/emdash-cms/emdash/blob/e7978d3da8884a1d829e2f64fc7cc7a534ca1f40/packages/cloudflare/src/plugins/ai-search.ts#L767-L780) dans la réponse d'AI Search afin qu'elles correspondent au format attendu par le snippet. Mauvaise nouvelle. J'ai également inspecté les réponses réseau de la recherche du blog de Cloudflare pour comprendre le format attendu, ce qui a confirmé ma découverte.

Je ne vais pas me laisser abattre. Trouvons d'abord un moyen de le faire fonctionner. Ouvrons ensuite quelques PR pour améliorer la situation. Enfin, publions une démo pour montrer à quel point le produit est génial lorsqu'il fonctionne.

J'ai fouillé dans le code source du snippet, en particulier dans celui de la modale, et j'ai découvert qu'il utilisait la clé de l'élément comme URL lorsqu'un utilisateur cliquait sur un résultat de recherche. Le problème est qu'une clé AI Search n'est pas nécessairement une URL publique. C'est un chemin vers l'élément dans le stockage. Ainsi, si vous téléversez un fichier nommé `my-file.md` dans le répertoire `docs`, la clé sera `docs/my-file.md`. Mais le snippet utilise cette clé comme lien. Cliquer sur le résultat ouvre donc `https://<your-domain>/docs/my-file.md`, ce qui n'est pas la bonne URL. Je voulais qu'il ouvre `https://<your-domain>/docs/my-file`.

Mais pourquoi ne pas simplement envoyer `/docs/my-file` comme clé ? Parce que ce n'est pas une clé d'élément AI Search valide. Les clés d'éléments ne peuvent pas commencer par `/` et doivent inclure une extension de fichier. Le snippet peut également différencier les pages et les sections pour améliorer l'expérience de recherche. C'est une bonne idée jusqu'à ce que vous réalisiez qu'un lien vers une section nécessite un fragment `#` dans l'URL publique, qui ne peut pas être représenté dans la clé de l'élément.

Je ne peux pas réécrire la clé à la volée, car la promesse du snippet est de l'utiliser avec l'endpoint public et de le faire fonctionner immédiatement, sans configuration ni serveur.

Au lieu d'utiliser la clé, j'ai donc décidé d'utiliser un champ de métadonnées personnalisé pour stocker l'URL. AI Search nous permet de définir des champs de métadonnées supplémentaires et d'associer leurs valeurs aux éléments téléversés. Cependant, cela nécessite également une PR sur le snippet pour fonctionner :

<PullRequest title="feat: use metadata url" href="https://github.com/cloudflare/ai-search-snippet/pull/44" />

J'ai donc ouvert la PR. Entre-temps, j'en ai également ouvert une autre pour mettre à jour le README obsolète :

<PullRequest title="docs: update readme" href="https://github.com/cloudflare/ai-search-snippet/pull/45" />

Dans les adaptateurs [VitePress](https://github.com/Barbapapazes/cloudflare-ai-search-sync/blob/08055be4ca257cd618064f9e9aa03d96b220ce6f/src/vitepress/utils.ts#L13) et [Nuxt](https://github.com/Barbapapazes/cloudflare-ai-search-sync/blob/08055be4ca257cd618064f9e9aa03d96b220ce6f/src/nuxt/utils.ts#L26), je déduis l'URL publique de chaque page à partir de son chemin source et je stocke cette URL dans les métadonnées personnalisées de l'élément.

J'ai également dû créer les [adaptateurs qui synchronisent le contenu](https://github.com/Barbapapazes/cloudflare-ai-search-sync) avec AI Search. Nous avions besoin de métadonnées personnalisées et voulions l'expérience de développement la plus simple possible, où il suffit d'installer l'intégration pour que tout fonctionne. R2 nécessitait des outils de téléversement supplémentaires et l'exploration impliquait d'ajouter des métadonnées à chaque page générée du site web. La meilleure option était d'utiliser l'API Items pour téléverser directement les fichiers Markdown et leurs métadonnées vers le stockage intégré.

Enfin, quelque chose fonctionnait ! Énorme !

Mais cela ne fonctionnait que dans mon environnement local. Dommage !

Tant que la [PR #44](https://github.com/cloudflare/ai-search-snippet/pull/44) reste ouverte, le snippet en amont continue d'utiliser la clé de l'élément comme URL. Mon environnement local utilisait la dépendance corrigée, mais le plugin publié chargeait dynamiquement le package en amont à la place. Pour que cela fonctionne aujourd'hui, j'ai dû intégrer le snippet corrigé au plugin. C'était un peu bricolé, car le module est chargé dynamiquement dans un composant Vue en dehors du pipeline de bundling habituel. L'ajouter à `alwaysBundle` dans la configuration de tsdown ne suffisait donc pas. J'ai dû faire preuve de plus de créativité.

Au final, cela fonctionne. Découvrez la démo VitePress sur [vitepress-plugin-cloudflare-ai-search.barbapapazes.dev](https://vitepress-plugin-cloudflare-ai-search.barbapapazes.dev) et la démo du module Nuxt sur [docus-cloudflare-ai-search.barbapapazes.dev](https://docus-cloudflare-ai-search.barbapapazes.dev).

L'expérience de développement de Cloudflare ne cesse de s'améliorer, mais les produits les plus récents peuvent encore nécessiter de fouiller dans le code source et de tester le système à plusieurs reprises lorsque la documentation et les intégrations évoluent à des rythmes différents.

Au final, cela fonctionne et je suis certain que l'équipe corrigera rapidement ces problèmes ! Ses chefs de produit sont vraiment ouverts aux retours de la communauté.

## Ce qu'il Faut Retenir

Premièrement, l'open source est formidable. Créez des projets open source et contribuez à l'open source. C'est l'une des meilleures façons d'apprendre et de progresser en tant que développeur. Mais n'oubliez pas qu'[il ne s'agit que d'un effet secondaire du fait de créer des choses](./want-to-contribute-to-open-source-youre-doing-it-wrong.md).

Deuxièmement, commencez par un périmètre restreint avant de l'élargir. J'ai commencé avec un module Nuxt, car c'était le moyen le plus simple de valider l'idée. Après avoir partagé une courte vidéo sur X et recueilli des retours, j'ai extrait le processus de synchronisation dans un package dédié et étendu l'intégration à VitePress. Fait intéressant, la version VitePress a été prête en premier et le module Nuxt a suivi plus tard.

Troisièmement, soyez persévérant et patient. Parfois, les choses ne fonctionnent pas comme prévu et sont plus difficiles qu'elles n'en ont l'air. Cela ne signifie toutefois pas qu'elles sont impossibles. En persévérant et en allant un peu plus loin, vous pouvez les faire fonctionner et la récompense en vaut toujours la peine.
