19 juillet 2023
Directus : le CMS headless open source
7 minutes de lecture
Dans le cadre d'une mission pour l'Office national de diffusion artistique, nous avons eu l'occasion de travailler avec le CMS headless Directus. Nous avons été séduits par sa simplicité d'utilisation et sa flexibilité. Nous vous proposons un retour sur les différents points techniques que nous avons aimés.
Open-source
Directus est un CMS headless (tel que Strapi) permettant de développer rapidement des backends/admin pour vos applications. Il offre une interface d'administration moderne et personnalisable, ainsi qu'une API REST / GraphQL pour consommer vos données.
L'un des points forts de Directus est d'être entièrement open-source. Directus propose une offre Cloud assez coûteuse (99$ par mois), mais aussi la possibilité d'héberger le CMS sans frais.
C'est bien sûr cette dernière option que nous avons choisie pour l'Onda afin d'avoir la main sur l'infrastructure et les coûts. Pour héberger Directus, il vous faudra une infrastructure classique permettant de faire tourner un serveur web Node.js.
Créer un projet
Initialiser un nouveau projet Directus est très simple grâce au CLI.
La commande init
permet d'installer les dépendances, configurer votre base de données (infos stockées dans un fichier .env
) et créer l'utilisateur admin :
npm init
npm add directus
npx directus init
? Choose your database client (Use arrow keys)
❯ PostgreSQL / Redshift
CockroachDB (Beta)
MySQL / MariaDB / Aurora
SQLite
Microsoft SQL Server
? Choose your database client PostgreSQL / Redshift
? Database Host: 127.0.0.1
? Port: 5432
? Database Name: directus
? Database User:
? Database Password:
? Enable SSL: (y/N)
Create your first admin user:
? Email xxx@xxxx.xx
? Password ****
Start Directus by running:
npx directus start
Ceci fait, vous pouvez lancer Directus :
npx directus start
Workflow de développement
Création du modèle de données
À l'image d'un CMS headless, Directus permet de créer vos modèles de données très simplement via une interface :
Créons par exemple une collection articles
avec différents champs :
Directus va alors créer une table articles
dans votre base de données avec les champs que vous avez définis.
Un des avantages de Directus est de bien séparer vos données métiers et les données de configuration liées à Directus (le fait que le champ content
doit-être du type markdown
par exemple). Ainsi tout ce qui est lié à Directus est stocké dans des tables préfixées par directus_
.
Versionning du schéma
Lors du cycle de développement, il est important de versionner votre modèle de données afin que différents développeurs puissent récupérer les changements. Pour cela, Directus offre deux commandes pour exporter et importer des snapshots via un fichier yaml ou json :
npx directus schema snapshot ./snapshot.yaml
// snapshot.yaml
version: 1
directus: 12.0.3
vendor: postgres
collections:
- collection: articles
meta:
accountability: all
archive_app_filter: true
archive_field: null
archive_value: null
collapse: open
collection: articles
color: null
display_template: null
group: null
hidden: false
icon: null
item_duplication_fields: null
note: null
preview_url: null
singleton: false
sort: null
sort_field: null
translations: null
unarchive_value: null
schema:
name: articles
// ...
Vous pouvez ainsi versionner ce schéma et récupérer les changements grâce à la commande apply
:
npx directus schema apply ./snapshot.yaml
Cette commande doit aussi être lancée lors de vos déploiements (via un CI par exemple) afin de mettre à jour le schéma de votre base de données.
OpenAPI et TypeScript
Spécification OpenAPI
Qui dit CMS headless, dit frontend pour consommer les données. Ce front peut-être par exemple une application Next.js qui va se connecter à l'API de Directus pour récupérer vos données. Il est alors fort utile d'avoir un contrat fort de typage entre l'api et le front. Nous pouvons utiliser la spécification OpenAPI générée par Directus et créer les types TypeScript associés.
Directus expose la spécification à l'adresse suivante :
http://0.0.0.0:8055/server/specs/oas?access_token=VOTRE_TOKEN
openapi "3.0.1"
info {…}
servers […]
paths {…}
tags […]
components {…}
Afin d'avoir accès à l'ensemble du schéma, il vous faudra générer un token statique depuis un utilisateur administrateur. On regrette le fait de ne pas pouvoir générer un token statique global sans passer par un utilisateur.
Il est important de relever que la spécification OpenAPI générée prend en compte les permissions liés au token. Ainsi sans token, si le rôle Public
a seulement accès en lecture aux endpoints de lecture des articles, uniquement ces derniers seront retournés dans la spécification :
On découvre par la même occasion la gestion très fine des rôles et permissions de Directus. Pour chaque rôle défini, nous pouvons définir les permissions CRUD sur chaque collection (et même aller sur une granularité plus fine grâce à un système de règles).
Typages avec TypeScript
Nous avons déjà évoqué le workflow de génération de types TypeScript depuis une spécification OpenAPI dans un précédent article.
Depuis votre front, vous pouvez utiliser la librairie swagger-typescript-api afin de générer les types associés à votre API :
swagger-typescript-api -p http://0.0.0.0:8055/server/specs/oas?access_token=VOTRE_TOKEN -o src/generated -n types.d.ts
Directus offre un SDK TypeScript pour facilement requêter votre API en utilisant vos types TS :
import { ItemsArticles } from '@/generated/types'
import { Directus } from '@directus/sdk'
type Collections = {
articles: ItemsArticles
}
const directus = new Directus<Collections>(process.env.NEXT_PUBLIC_API_HOST)
const articles = await directus
.items('articles')
.readByQuery({ filter: { title: { _contains: 'Hello world' } } })
Next.js et génération statique
Le site de l'Onda est une application Next.js 13 qui vient consommer directement l'API exposée par Directus. Les pages sont générées statiquement lors du build puis sont regénérées automatiquement lorsque des changements sont détectés.
Un tel workflow peut être mis en place facilement avec le mécanisme de revalidation de Next.js ainsi que les flux webhooks de Directus.
Route d'API Next.js côté front pour regénérer une page :
import { revalidatePath } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
if (!request.url) {
return new Response('Invalid request', { status: 400 })
}
const { searchParams } = new URL(request.url)
const secret = searchParams.get('secret')
const path = searchParams.get('path')
if (!secret || secret !== process.env.INVALIDATE_SECRET_TOKEN || !path) {
return new Response('Invalid credentials or missing path', {
status: 400,
})
}
revalidatePath(path)
return NextResponse.json({
revalidated: true,
now: Date.now(),
})
}
Mise en place d'un flux d'invalidation côté Directus :
Un système d'extensions complet
Directus offre également la possibilité d'étendre ses fonctionnalités grâce à un système d'extensions. Il est possible de créer plusieurs types d'extensions, parmi lesquels :
- Des endpoints personnalisés ;
- Des listeners / hooks sur les évènements ;
- Des widgets personnalisés dans les formulaires ;
Cela permet de facilement étendre Directus pour répondre à des besoins spécifiques. À noter que Directus est développé en Vue.js : toutes les interfaces doivent donc être développées avec cette librairie. En tant qu'agence spécialisée en React, nous aurions aimé un binding agnostique et pouvoir développer nos interfaces avec React.
Nous avons aussi utilisé Prisma au sein des extensions de type serveur (endpoints, hooks, listeners…) afin de bénéficier d'un query builder TypeScript puissant. Grâce à la commande pull
, nous pouvons récupérer le schéma et bénéficier du client Prisma :
prisma db pull
prisma generate
Derniers mots
Les points forts
Pour finir voici les autres points qui nous ont séduits dans Directus :
- Un écosystème vivace : des releases régulières, une communauté active et une documentation de qualité ;
- Un système de permissions complet : pour un rôle donné, il est possible de définir des permissions avec une granularité très fine ;
- Une UX/UI de qualité : Directus est très agréable à utiliser et offre une UX moderne avec le souci du détail ;
- Un haut niveau de personnalisation : grâce à de nombreuses options, il est possible de personnaliser l'interface de Directus pour répondre à vos besoins sans trop de développements custom ;
- Un système de workflows : grâce à un outil no-code intégré, vous pouvez facilement mettre en place des workflows sur vos données métiers ;
- Une gestion des traductions : gérer plusieurs langues dans vos collections est très simple avec Directus ;
Ce que nous aimons moins
- Interfaces des extensions en Vue.js plutôt que React.js, mais c'est personnel 😁 ;
- Des conflits GIT sur le fichier de snapshot du schéma parfois assez complexes à résoudre ;
Beaucoup de points positifs donc pour Directus : un outil que nous ajoutons à notre stack technique avec grand plaisir ! Nous pensons donc à l'avenir partir sur Directus plutôt que Strapi. En effet, Directus offre une meilleure UX/UI/DX, un écosystème plus mature et permet de mieux s'adapter aux règles métiers de nos clients.