<style>
section {
text-align: left;
font-size: 22px;
}
code {
font-size: 16px;
}
</style>
# Directus, un CMS Headless
> [name=Philippe Depouilly]
> Institut de Mathématiques de Bordeaux
> Journées Mathrice
> XLIM Limoges 2025
---
# En quelques mots
- un CMS Headless
- découplage backend/frontend
- correspondance Modèle de données <-> APIs
- modèle relationnel de la forme ManyToOne, OneToMany, ManyToMany (ORM)
- bibliothèques Javascript intégrées et riches
- fonctionne aussi bien avec Mysql, Postgresql, etc.
- un backend complet et fortement customisable
---
## Installer Directus
### Création du projet
``` bash
oc login --web https://api.math.cnrs.fr:6443
oc new-project directus-journees
```
---
### Déploiement de Directus
``` bash
helm repo add directus https://directus-labs.github.io/helm-chart/
helm repo update
```
``` yaml
adminEmail: pdepouil@math.u-bordeaux.fr
extraVolumeMounts:
- mountPath: /.pm2
name: pm2
extraVolumes:
- emptyDir: {}
name: pm2
# Pour le développement
extraEnvVars:
- name: CORS_ENABLED
value: 'true'
- name: CORS_ORIGIN
value: 'http://localhost:3000'
ingress:
enabled: true
className: openshift-default
annotations:
route.openshift.io/termination: edge
hosts:
- host: directus-journees.apps.math.cnrs.fr
paths:
- path: /
pathType: Prefix
```
``` bash
helm install directus directus/directus -f values.yaml
```
---
### Directus : tour du propriétaire
Récupération du mot de passe
``` bash
oc get secret directus-application-secret -o jsonpath="{.data.ADMIN_PASSWORD}"|base64 -d
```
Et hop... https://directus-journees.apps.math.cnrs.fr
Créer un premier modèle `global` avec les champs `title` et `description` et le rendre public
---
## NuxtJS
``` bash
$ npx nuxt init my-website
$ cd my-website
$ npm install
$ npm install nuxt-directus
```
nuxt.config.ts :
``` javascript
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-05-15',
devtools: { enabled: true },
+ modules: ["nuxt-directus"],
+ directus: {
+ url: "https://directus-journees.apps.math.cnrs.fr/"
+ },
})
```
---
app.vue :
``` javascript
<template>
<div>
<NuxtRouteAnnouncer />
- <NuxtWelcome />
+ <NuxtPage />
</div>
</template>
```
---
### Première page
pages/index.vue
``` javascript
<template>
<h1>{{global.title}}</h1>
<p>{{global.description}}</p>
</template>
<script setup lang="ts">
const { getSingletonItem } = useDirectusItems();
interface Global {
title: string;
description: string;
}
const global = await getSingletonItem<Global>({
collection: "global"
});
</script>
```
---
### Test sur le poste de travail
``` bash
npm run dev
```
sur http://localhost:3000
---
### Ajouter des pages sur le Blog
Dans Directus ajouter un model pages avec les champs `slug`, `title` et `content`, le rendre public
pages/[slug].vue :
``` javascript
<template>
<h1>{{page.title}}</h1>
<div>{{page.content}}</div>
</template>
<script setup lang="ts">
const { getItemById } = useDirectusItems();
const route = useRoute()
interface Page {
slug: string;
title: string;
content: string;
}
const page = await getItemById <Page>({
collection: "pages",
id: route.params.slug
});
if (!page.content) throw createError({
statusCode: 404,
statusMessage: 'Page Not Found'
})
</script>
```
---
Importer les Pages depuis un CSV et tester :
http://localhost:3000/about
---
### Ajouter des nouvelles dans le Blog
Créer un modèle `authors` public avec un champ `name`
Créer un modèle `blogs` public avec les champs `slug`, `title`, `content`, `image`, `publish_date` et une relation one-to-many de `author `vers `authors`
---
Dans pages/blog/index.vue
``` ruby
<template>
<h1>Blog</h1>
<ul>
<template v-for="post in posts" :key="post.id">
<li>
<NuxtLink :href="`/blog/${post.slug}`">
<h2>{{post.title}}</h2>
</NuxtLink>
<span>{{post.publish_date}} • {{post.author.name}}</span>
</li>
</template>
</ul>
</template>
<script setup lang="ts">
const { getItems } = useDirectusItems();
interface Post {
slug: string;
title: string;
publish_date: string;
name: string;
}
const fields = [
'slug',
'title',
'publish_date',
'author.name',
]
const posts = await getItems <Post>({
collection: "blogs",
params: {
fields: fields,
sort: ['-publish_date']
}
});
</script>
```
---
Puis une affichage par Post dans pages/blog/\[slug\].vue
```javascript=
<template>
<h1>{{post.title}}</h1>
<div>{{post.content}}</div>
</template>
<script setup lang="ts">
const { getItemById } = useDirectusItems();
const route = useRoute()
interface Post {
slug: string;
title: string;
content: string;
}
const post = await getItemById <Post>({
collection: "blogs",
id: route.params.slug
});
if (!post.content) throw createError({
statusCode: 404,
statusMessage: 'Post Not Found'
})
</script>
```
---
Importer les Pages depuis un CSV et tester :
http://localhost:3000/blog
---
Et enfin un menu dans app.vue
```javascript=
<template>
<div>
<NuxtRouteAnnouncer />
<nav>
<NuxtLink to="/">Home </NuxtLink>
-
<NuxtLink to="/about"> About </NuxtLink>
-
<NuxtLink to="/conduct"> Code of Conduct </NuxtLink>
-
<NuxtLink to="/privacy"> Privacy Policy</NuxtLink>
-
<NuxtLink to="/blog"> Blog</NuxtLink>
</nav>
<NuxtPage />
</div>
</template>
```
---
## Déploiement de Nuxt sur PLMshift
``` bash
git push --set-upstream git@plmlab.math.cnrs.fr:philippe.depouilly/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)
```
Mettre le dépôt public afin de le déployer sur PLMshift :
``` bash
oc new-app nodejs:20-ubi9-minimal -e NPM_RUN=dev -e NUXT_PORT=8080 -e NUXT_HOST=0.0.0.0 https://plmlab.math.cnrs.fr/philippe.depouilly/my-website.git
```
``` bash
oc create route edge --service=my-website --hostname=nuxt-journees.apps.math.cnrs.fr
```
---
### Modification Directus
``` javascript
adminEmail: pdepouil@math.u-bordeaux.fr
extraVolumeMounts:
- mountPath: /.pm2
name: pm2
extraVolumes:
- emptyDir: {}
name: pm2
# Pour la prod
extraEnvVars:
- name: CORS_ENABLED
value: 'true'
- name: CORS_ORIGIN
value: 'https://nuxt-journees.apps.math.cnrs.fr
ingress:
enabled: true
className: openshift-default
annotations:
route.openshift.io/termination: edge
hosts:
- host: directus-journees.apps.math.cnrs.fr
paths:
- path: /
pathType: Prefix
```
Puis ajouter un Webhook gitlab
Il est maintenant possible de développer depuis le dépôt Git et le site se met à jour...
---
Ce qui n'a pas été abordé:
- l'authentification
- l'écriture createItem, deleteItem, etc.
- le stockage de fichiers (upload de medias)
- le templating des formulaires Directus
- etc.
---
Références
- https://directus.io
- https://directus.io/docs/tutorials/getting-started/fetch-data-from-directus-with-nuxt
- https://docs.directus.io/blog/nuxt-directus-getting-started.html
{"type":"slide","slideOptions":{"theme":"solarized","transition":"fade","controls":true}}