Introduction
Confluence, l'outil de collaboration Atlassian, est indispensable pour centraliser la documentation d'entreprise, les wikis techniques et les playbooks DevOps. En 2026, avec l'essor des pipelines CI/CD et des automatisations IA, maîtriser son API REST devient critique pour scaler les workflows. Imaginez générer automatiquement des pages de release notes depuis Jira, importer des rapports depuis GitHub, ou migrer des milliers de pages sans intervention manuelle.
Ce tutoriel avancé vous guide pas à pas pour créer un bot Node.js qui interagit avec l'API Confluence Cloud : authentification token-based, création de spaces/pages, uploads d'attachments, queries CQL avancées et gestion d'erreurs robuste. Chaque script est complet et fonctionnel, prêt à copier-coller. À la fin, vous automatiserez vos flux comme un pro, économisant des heures par sprint. Idéal pour architectes DevOps ou admins Atlassian gérant des instances >10k pages.
Prérequis
- Compte Confluence Cloud Premium ou Enterprise (API rate limits plus élevés)
- Node.js 20+ installé (vérifiez avec
node -v) - Éditeur de code comme VS Code avec extension TypeScript
- Connaissances avancées en TypeScript, API REST et async/await
- Accès admin ou permission 'Create Space' sur Confluence
Initialiser le projet Node.js
mkdir confluence-bot
cd confluence-bot
npm init -y
npm install axios dotenv typescript ts-node @types/node
npm install -D @types/axios
npx tsc --init
mkdir srcCe script bash crée un projet Node.js dédié, installe Axios pour les appels HTTP, Dotenv pour les variables d'environnement, et configure TypeScript. Il prépare un setup zéro-friction avec ts-node pour exécuter directement les .ts. Évitez les globals : utilisez toujours un package.json dédié.
Obtenir votre token API Confluence
Allez dans Atlassian Account Settings > Security > API Tokens > Create token. Nommez-le 'confluence-bot-2026'. Copiez le token (format ~abcd1234). Utilisez votre email Atlassian comme username pour Basic Auth. Stockez-les sécuritairement dans .env, jamais en dur. Rate limit : 100 req/min en Premium, surveillez les headers X-Seraph-LoginReason pour debug.
Configurer package.json
{
"name": "confluence-bot",
"version": "1.0.0",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "ts-node --watch src/index.ts"
},
"dependencies": {
"axios": "^1.7.2",
"dotenv": "^16.4.5",
"typescript": "^5.5.3",
"ts-node": "^10.9.2",
"@types/node": "^22.3.0"
},
"devDependencies": {
"@types/axios": "^0.14.0"
}
}Ce package.json complet ajoute des scripts start et dev pour lancer le bot facilement. Axios gère les requêtes HTTPS avec retry auto, Dotenv charge les secrets. TypeScript strict pour catcher les erreurs compile-time. Lancez avec npm run dev pour hot-reload en dev.
Fichier d'environnement .env
CONFLUENCE_BASE_URL=https://votre-site.atlassian.net/wiki
CONFLUENCE_EMAIL=votre.email@entreprise.com
CONFLUENCE_API_TOKEN=ATATT3x...VotreTokenComplet
SPACE_KEY=MYSPACERemplacez par vos vraies valeurs : BASE_URL sans /api, SPACE_KEY du space cible (trouvé via /spaces). Ajoutez .env à .gitignore. Dotenv le charge auto. Piège : URL sans 'https://' ou trailing slash cause 404 ; testez avec curl d'abord.
Implémenter l'authentification et lister les spaces
L'API Confluence utilise Basic Auth (email:token base64). Testez d'abord manuellement : curl -u email:token $BASE_URL/rest/api/space. Notre client Axios encapsule ça. Ce premier script liste les spaces pour valider l'auth, avec gestion d'erreurs 401/429.
Client API de base avec auth
import axios, { AxiosInstance, AxiosError } from 'axios';
import dotenv from 'dotenv';
dotenv.config();
const config = {
baseURL: process.env.CONFLUENCE_BASE_URL + '/rest/api',
auth: {
username: process.env.CONFLUENCE_EMAIL!,
password: process.env.CONFLUENCE_API_TOKEN!
},
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
};
export const client: AxiosInstance = axios.create(config);
client.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
if (error.response?.status === 429) {
console.error('Rate limit atteinte, attendez 1min');
throw error;
}
console.error('Erreur API:', error.response?.data || error.message);
throw error;
}
);
export async function listSpaces(): Promise<any[]> {
const res = await client.get('/space');
return res.data.results;
}
// Test
(async () => {
try {
const spaces = await listSpaces();
console.log('Spaces:', spaces.map((s: any) => ({ key: s.key, name: s.name })));
} catch (err) {
console.error('Échec:', err);
}
})();Ce client Axios réutilisable gère auth Basic, baseURL, headers et intercepteur pour rate limits (retry manuel requis). listSpaces() fetch tous les spaces avec paginaison implicite. Lancez npm run start : output JSON valide ou erreur debuggée. Piège : token expiré → 401, régénérez-le.
Créer un space programmatiquement
Permissions requises : 'Create Space' global. L'endpoint /space POST crée un space 'global' ou 'personal'. Utilisez type: 'global' pour équipes. Réponse inclut id et key pour refs futures.
Fonction créer space
import { client } from './confluenceClient';
export async function createSpace(name: string, key: string): Promise<any> {
const payload = {
type: 'global',
status: 'current',
name,
key,
description: { plain: { value: 'Space créé via API en 2026', representation: 'plain' } }
};
const res = await client.post('/space', payload);
console.log('Space créé:', res.data);
return res.data;
}
// Usage
(async () => {
await createSpace('Mon Bot Space', 'BOTSPACE');
})();Payload minimal mais complet : key unique (majuscules, no spaces). Description en 'plain' évite parsing HTML. Retourne l'objet space full. Erreur courante : key existant → 409 Conflict, checkez d'abord via listSpaces(). Intégrez en pipeline GitHub Actions.
Gérer les pages et contenus
Les pages sont des 'content' de type 'page'. Créez sous un parent (root ou page existante). Utilisez body.storage pour HTML riche, ou editor2 pour ADF (nouveau format 2026). Versionning auto.
Créer et updater une page
import { client } from './confluenceClient';
export async function createPage(spaceKey: string, title: string, bodyHtml: string, parentId?: string): Promise<any> {
const payload = {
type: 'page',
status: 'current',
space: { key: spaceKey },
title,
body: {
storage: {
value: bodyHtml,
representation: 'storage'
}
},
...(parentId && { parent: { id: parentId } })
};
const res = await client.post('/content', payload);
console.log('Page créée ID:', res.data.id);
return res.data;
}
export async function updatePage(pageId: string, version: number, bodyHtml: string): Promise<any> {
const payload = {
id: pageId,
type: 'page',
title: 'Titre mis à jour',
version: { number: version + 1 },
body: {
storage: { value: bodyHtml, representation: 'storage' }
}
};
const res = await client.put(`/content/${pageId}`, payload);
console.log('Page updatée, nouvelle version:', res.data.version.number);
return res.data;
}
// Usage exemple
(async () => {
const page = await createPage('BOTSPACE', 'Page Bot Test', '<h1>Hello 2026!</h1><p>Automatisé.</p>');
await updatePage(page.id, page.version.number, '<h1>Mis à jour!</h1><p>Version 2.</p>');
})();Création avec parentId pour hiérarchie. Update incrémente version obligatoirement (fetch via GET /content/{id} d'abord). HTML valide en 'storage' (macro support). Piège : body sans 'representation' → 400 Bad Request. Scalez pour 100+ pages en batch.
Uploader un attachment
import { client } from './confluenceClient';
import fs from 'fs';
export async function uploadAttachment(pageId: string, filePath: string): Promise<any> {
if (!fs.existsSync(filePath)) throw new Error('Fichier non trouvé');
const formData = new FormData();
formData.append('file', fs.createReadStream(filePath), { filename: filePath.split('/').pop() });
formData.append('comment', 'Upload auto 2026');
const res = await client.post(`/content/${pageId}/child/attachment`, formData, {
headers: formData.getHeaders()
});
console.log('Attachment uploadé:', res.data);
return res.data;
}
// Ajoutez npm install form-data @types/form-data pour FormData polyfill si besoin
// Usage
// await uploadAttachment('123456', './rapport.pdf');Utilise FormData pour multipart upload sur /child/attachment. fs.createReadStream pour gros fichiers (>100MB OK en Enterprise). Comment auto pour audit trail. Piège Node : FormData natif ES2023, sinon installez 'form-data'. Versionne attachments comme pages.
Queries avancées avec CQL
CQL (Confluence Query Language) filtre comme SQL : type=page and space=KEY and text~"mot-clé". Paginer avec start/limit. Idéal pour rapports automatisés ou cleanups.
Query CQL pour pages
import { client } from './confluenceClient';
export async function searchPages(cql: string, start = 0, limit = 25): Promise<any> {
const params = new URLSearchParams({ cql, start: start.toString(), limit: limit.toString() });
const res = await client.get(`/content/search?${params.toString()}`);
console.log(`Résultats (${res.data.size}/${res.data.totalSize}):`, res.data.results.map((p: any) => ({ id: p.id, title: p.title }))) ;
return res.data;
}
// Usage avancées
(async () => {
await searchPages('type=page and space=BOTSPACE and created >= -1d');
await searchPages('type=page and text ~ "erreur"', 0, 100);
})();CQL full-text search avec opérateurs (and, ~ fuzzy). Pagination manuelle pour >500 résultats. totalSize pour loops. Performant vs GET /space/{key}/page. Piège : CQL malformé → 400 ; testez dans Confluence UI 'Advanced Search' d'abord.
Bonnes pratiques
- Rate limiting : Implémentez exponential backoff (p-setTimeout) pour >50 req/min ; utilisez webhooks pour events push au lieu de polling.
- Sécurité : Rotatez tokens tous 90j ; utilisez App Links OAuth2 pour apps server-side (plus scalable que Basic).
- Error handling : Loggez toujours
error.response.data; retry idempotents (POST/PUT) max 3x. - Scalabilité : Batch via
/bulkendpoint ; migrez vers GraphQL beta si Premium+. - Tests : Mockez Axios avec MSW ; CI/CD avec Playwright pour end-to-end.
Erreurs courantes à éviter
- 401 Unauthorized : Token invalide/expiré ou email faux ; vérifiez base64
echo -n 'email:token' | base64. - 409 Conflict : Space/page key dupliqué ou version obsolète ; fetch latest avant update.
- 413 Payload Too Large : Attachments >2GB foirent ; chunker ou utilisez S3 proxy.
- CQL vide : Toujours
limit<100sinon timeout ; pas de wildcards%(utilisez~).
Pour aller plus loin
- Docs officielles : API Confluence REST
- Plugins avancés : ScriptRunner pour Groovy in-app ; Forge pour apps custom.
- Intégrez avec Jira :
/rest/api/3/issuesync. - Découvrez nos formations Learni sur Atlassian DevOps pour CI/CD complets.
- Repo GitHub exemple : fourni en fin d'article (forkez-le !)