iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
TypeScript · Avancé

OpenCaddyHost

Construire un dashboard Nuxt 4 pour piloter Caddy via son Admin API — authentification JWT, reverse proxy CRUD, import/export, et déploiement Docker Compose.

Nuxt 4Vue 3TypeScriptPiniaCaddyDocker
GitHub
01Setup et configuration02L'API Admin de Caddy03Server routes Nuxt 404Authentification JWT05Client Caddy et gestion des proxies06Interface et composants Vue07Déploiement Docker Compose
Chapitre 3·25 min

Server routes Nuxt 4

Nuxt 4 intègre un serveur HTTP (Nitro) qui tourne côté serveur. Les fichiers dans server/api/ deviennent automatiquement des endpoints REST — pas besoin d'Express, pas de fichier de routing.

Convention de nommage

Le nom du fichier définit la route et la méthode HTTP :

server/api/auth.post.ts        → POST  /api/auth
server/api/auth/verify.get.ts  → GET   /api/auth/verify
server/api/proxies.get.ts      → GET   /api/proxies
server/api/proxies.post.ts     → POST  /api/proxies
server/api/health.get.ts       → GET   /api/health
server/api/caddy/health.get.ts → GET   /api/caddy/health

Un même fichier peut gérer plusieurs méthodes si on omet le suffixe .method et qu'on exporte un handler default avec un switch sur getMethod(event).

Anatomie d'un event handler

server/api/health.get.ts
export default defineEventHandler(async (event) => {
  return {
    status: 'ok',
    timestamp: new Date().toISOString(),
    version: '1.0.0',
  }
})

defineEventHandler est le wrapper Nitro/h3. Il reçoit un event qui contient la requête, la réponse, et les utilitaires.

Lire le body d'une requête POST

server/api/proxies.post.ts
import { readBody } from 'h3'
 
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  // body est déjà parsé depuis le JSON
  const { name, domain, target } = body
  // ...
})

readBody parse automatiquement le JSON. Pas de JSON.parse manuel.

Lire les query params

server/api/proxies.get.ts
import { getQuery } from 'h3'
 
export default defineEventHandler(async (event) => {
  const { search, status } = getQuery(event)
  // search et status viennent de ?search=...&status=...
})

Lire les headers

import { getHeader } from 'h3'
 
export default defineEventHandler(async (event) => {
  const authHeader = getHeader(event, 'authorization')
  // "Bearer eyJhbGci..."
})

Accéder à runtimeConfig

server/api/auth.post.ts
export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  // config.jwtSecret, config.adminUsername, config.adminPassword
  // config.caddyAdminUrl
})

useRuntimeConfig() côté serveur expose les secrets. Côté client, seul config.public.* est accessible.

Lever une erreur HTTP

import { createError } from 'h3'
 
throw createError({
  statusCode: 401,
  statusMessage: 'Non autorisé',
})

Nitro sérialise l'erreur en JSON et renvoie le bon status HTTP. Pas besoin de res.status(401).json(...).

Utilitaires serveur partagés

Les fichiers dans server/utils/ sont auto-importés dans tous les handlers. C'est là que vit le client Caddy et la vérification JWT :

server/utils/auth.ts
import jwt from 'jsonwebtoken'
import { getHeader, createError } from 'h3'
 
export function authenticateToken(event: H3Event) {
  const config = useRuntimeConfig()
  const authHeader = getHeader(event, 'authorization')
 
  if (!authHeader?.startsWith('Bearer ')) {
    throw createError({ statusCode: 401, statusMessage: 'Token manquant' })
  }
 
  const token = authHeader.slice(7)
 
  try {
    return jwt.verify(token, config.jwtSecret) as JwtPayload
  } catch {
    throw createError({ statusCode: 403, statusMessage: 'Token invalide' })
  }
}

authenticateToken(event) s'appelle en première ligne de chaque handler protégé :

server/api/proxies.get.ts
export default defineEventHandler(async (event) => {
  const user = authenticateToken(event) // 401/403 si invalide
 
  // ici, l'utilisateur est authentifié
  const proxies = await getCaddyProxies()
  return { success: true, data: proxies }
})

Middleware serveur

Pour des traitements transversaux (logging, CORS), créer server/middleware/ :

server/middleware/cors.ts
export default defineEventHandler((event) => {
  setResponseHeaders(event, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  })
})

Ce middleware s'exécute sur toutes les requêtes, avant les handlers. Pas de config de routing.

Précédent
L'API Admin de Caddy
Suivant
Authentification JWT

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main