Intégrer Cloudflare AI Search à Docus et VitePress
En juillet 2025, NuxtLabs a rejoint 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 sur le framework. Il en va de même pour les primitives UnJS sous-jacentes, grâce au travail de Pooya.
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.
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 !
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 ou le dépôt GitHub.
Pour utiliser le module, vous devez l'installer :
pnpm add docus-cloudflare-ai-searchVous devez ensuite ajouter le module à votre fichier nuxt.config.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. 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 :
- Utiliser le stockage intégré, alimenté depuis le tableau de bord ou l'API Items
- Connecter un bucket R2
- 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 :
Installez le package :
pnpm add cloudflare-ai-search-syncAjoutez ensuite le module à votre fichier nuxt.config.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 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, 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é : 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 :
pnpm add vitepress-plugin-cloudflare-ai-searchVous devez ensuite ajouter le plugin à votre fichier config.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, qui s'en chargera automatiquement.
Installez le package :
pnpm add cloudflare-ai-search-syncAjoutez ensuite le plugin à votre fichier config.ts :
import { cloudflareAISearchSync } from 'cloudflare-ai-search-sync/vitepress'
import { defineConfig } from 'vitepress'
export default defineConfig({
buildEnd: cloudflareAISearchSync({ enabled: true }),
})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 ou le dépôt GitHub.
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 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 :
- Utiliser le stockage intégré via le tableau de bord ou l'API Items
- Connecter un bucket R2
- 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 et du snippet d'interface. 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 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 :
J'ai donc ouvert la PR. Entre-temps, j'en ai également ouvert une autre pour mettre à jour le README obsolète :
Dans les adaptateurs VitePress et Nuxt, 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 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 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 et la démo du module Nuxt sur 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.
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.
Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web et le parcours humain qui l'entoure.
Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.
Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.
J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous, cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !
Discussions
Ajouter un commentaire
Vous devez être connecté pour accéder à cette fonctionnalité.