Van Drupal 9 naar Directus + Nuxt

Van Drupal 9 naar Directus + Nuxt

Sjoerd 2 februari 2026

Blogpost met een plan voor de migratie van Drupal 9 website naar een nieuwe Directus + Nuxt website. Het is maar waar je zin in hebt.

Inleiding

De oude Drupal 9.3.22 site voor bleekneusjes.nl scheuren te vertonen. De combinatie van PHP 8.2 en een verouderde Drupal versie zorgt voor deprecation warnings die inloggen blokkeren. Tijd voor een moderne aanpak.

In plaats van de moeizame upgrade path via Drupal 10 en 11 te volgen zonder gebruik te kunnen maken van de terminal, grijp ik deze kans aan om te migreren naar een moderne headless CMS stack: Directus als backend en Nuxt 3 als frontend. Een goede gelegenheid om mijn nieuwe onderzoek-stack uit te proberen op een productiesite.

Waarom deze stack?

Drupal upgrade = technische schuld De weg van Drupal 9.3 naar 11 is lang en pijnlijk. Elke major versie brengt breaking changes, deprecated APIs, en module incompatibiliteiten. Voor een simpele archief site met 7 pagina's is dit pure overkill.

Directus + Nuxt = moderne simpliciteit

  • Directus: Open-source headless CMS met een uitstekende API-first aanpak
  • Nuxt 3: Modern Vue framework met SSR/SSG support
  • Schone scheiding tussen content (Directus) en presentatie (Nuxt)
  • Perfect voor een simpele site die vooral wordt bezocht als archief

De situatie

Huidige site:

  • 7 pagina's met HTML content en veel afbeeldingen
  • Links naar Flickr fotoselecties (belangrijkste functie)
  • 2 gebruikers
  • Facebook Likebox integratie
  • Weinig updates verwacht (archief karakter)

Het probleem:

  • PHP 8.2 compatibility issues
  • Drupal 9.3 is verouderd
  • Shared hosting met beperkte controle -> Geen commandline toegang en dus geen drush en composer
  • Andere sites op zelfde account maken PHP downgrade lastig

De oplossing: Migratie naar VPS met Directus + Nuxt stack

Het migratieplan: 7 fasen

Fase 1: Voorbereiding & Analyse

Content audit:

  • Inventariseer alle 7 pagina's en hun content structuur
  • Tel alle afbeeldingen en bepaal opslagbehoefte
  • Analyseer Facebook Likebox implementatie
  • Maak complete backup van database en files

VPS check:

  • Node.js versie (minimaal 18 voor Nuxt 3)
  • PostgreSQL database beschikbaar
  • Nginx configuratie check
  • Subdomain opzetten voor development (test.bleekneusjes.nl)

Fase 2: Directus Setup

Installatie:

  • Nieuwe Directus instantie via npm/Docker
  • PostgreSQL database aanmaken
  • Admin user configureren
  • Public URL en uploads configureren

Content model:

Collection: pages
- id (UUID)
- title (String)
- slug (String, unique)
- content (WYSIWYG/HTML)
- images (Many-to-Many naar files)
- featured_image (Many-to-One naar files)
- meta_description (Text)
- published (Boolean)
- sort_order (Integer)

User management:

  • 2 gebruikers aanmaken met editor rol
  • Permissions instellen (alleen eigen content model)

Fase 3: Content & Media Migratie

Afbeeldingen downloaden: Via SFTP alle bestanden uit /sites/default/files/ halen en lokaal organiseren.

Content export: Voor 7 pagina's is handmatig kopiëren prima werkbaar:

  1. Open Drupal pagina in edit mode
  2. Kopieer HTML content
  3. Plak in Directus WYSIWYG editor
  4. Link afbeeldingen naar geüploade assets

Alternatief: Script schrijven dat via Drupal REST API content haalt en via Directus API importeert (leerzaam voor toekomstige migraties).

Flickr links: Alle externe links naar Flickr blijven gewoon werken in de HTML content.

Fase 4: Nuxt Frontend

Project setup:

npx nuxi@latest init bleekneusjes-frontend
cd bleekneusjes-frontend
npm install @directus/sdk

Directory structuur:

/pages
  /index.vue                 # Homepage
  /[slug].vue               # Dynamic pagina's
/components
  /Navigation.vue           # Hoofdmenu
  /FacebookWidget.vue       # Facebook integratie
  /Footer.vue
/composables
  /useDirectus.ts          # Directus API client
/public
  /favicon.ico

Directus integratie:

// composables/useDirectus.ts
import { createDirectus, rest, readItems } from '@directus/sdk'

export const useDirectus = () => {
  const config = useRuntimeConfig()
  const directus = createDirectus(config.public.directusUrl).with(rest())
  
  return { directus }
}

SSG configuratie: Voor een archief site met weinig updates is Static Site Generation ideaal:

  • Snelste laadtijden
  • Geen server-side processing
  • Kan op simpele hosting (maar we gebruiken VPS)
  • Rebuild bij content updates (makkelijk te automatiseren)

Fase 5: Facebook Integratie

De oude Drupal Facebook Likebox module vervangen we door de moderne Facebook Page Plugin:

<!-- components/FacebookWidget.vue -->
<template>
  <div class="fb-container">
    <div 
      class="fb-page" 
      data-href="https://www.facebook.com/[page-name]"
      data-width="340"
      data-hide-cover="false"
      data-show-facepile="true"
    ></div>
  </div>
</template>

<script setup>
onMounted(() => {
  // Load Facebook SDK
  const script = document.createElement('script')
  script.src = 'https://connect.facebook.net/nl_NL/sdk.js#xfbml=1&version=v18.0'
  document.body.appendChild(script)
})
</script>

Fase 6: Testing & Performance

Checklist:

  • Alle 7 pagina's renderen correct
  • Afbeeldingen laden (check in DevTools Network tab)
  • Flickr links werken
  • Facebook widget laadt
  • Mobile responsive (test op verschillende schermen)
  • Lighthouse score > 90 (SSG maakt dit makkelijk)

SEO:

// pages/[slug].vue
const page = await getPage(route.params.slug)

useHead({
  title: page.title,
  meta: [
    { name: 'description', content: page.meta_description },
    { property: 'og:title', content: page.title },
    { property: 'og:image', content: page.featured_image?.url }
  ]
})

Fase 7: Deployment & Go-live

Systemd service setup: Zoals mijn andere Directus/Nuxt blog op de VPS, gebruiken we systemd voor process management.

Directus service:

[Unit]
Description=Directus Bleekneusjes
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/bleekneusjes-directus
ExecStart=/usr/bin/node /var/www/bleekneusjes-directus/node_modules/.bin/directus start
Restart=on-failure

[Install]
WantedBy=multi-user.target

Nuxt service:

[Unit]
Description=Nuxt Bleekneusjes
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/bleekneusjes-frontend
ExecStart=/usr/bin/node .output/server/index.mjs
Restart=on-failure
Environment=PORT=3001

[Install]
WantedBy=multi-user.target

Nginx configuratie:

server {
    server_name bleekneusjes.nl www.bleekneusjes.nl;
    
    location / {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    server_name directus.bleekneusjes.nl;
    
    location / {
        proxy_pass http://localhost:8055;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
    }
}

DNS cutover:

  1. Test eerst op subdomain (test.bleekneusjes.nl)
  2. Verlaag TTL van huidige DNS record naar 300 seconden
  3. Wacht 24 uur
  4. Update A-record naar VPS IP
  5. Monitor eerste uren op errors

Post-launch:

  • Oude Drupal site blijft 1 maand beschikbaar als backup
  • Gebruikers krijgen korte training in Directus interface
  • Documentatie schrijven voor content updates

Tijdsinvestering

  • Fase 1: 2-3 uur
  • Fase 2: 3-4 uur
  • Fase 3: 4-6 uur
  • Fase 4: 8-12 uur
  • Fase 5: 1-2 uur
  • Fase 6: 3-4 uur
  • Fase 7: 2-3 uur

Totaal: 23-34 uur gespreid over 2-4 weken

Beslissingen

Database: PostgreSQL (betere performance, JSONB support)
Render mode: SSG (archief site, weinig updates)
Deployment: systemd services (zoals bestaande blog)
Content import: Handmatig (7 pagina's) of geautomatiseerd (leerzaam)
URL structuur: Nieuwe clean URLs, oude hoeven niet behouden (klein archief)

Voordelen van deze aanpak

Technisch:

  • Moderne, onderhoudbare codebase
  • API-first architectuur (future-proof)
  • Snelle performance door SSG
  • Volledige controle op VPS

Praktisch:

  • Schone lei, geen legacy code
  • Eenvoudige content updates via Directus UI
  • Leren door doen (Directus + Nuxt ervaring)
  • Schaalbaar voor toekomstige projecten

Onderhoud:

  • Geen PHP version gymnastics meer
  • npm update voor dependencies
  • Content en code volledig gescheiden

Risico's & Mitigaties

Content verlies: Multiple backups op verschillende momenten
Downtime: Test op subdomain, lage DNS TTL, parallel development
Facebook widget: Vroeg testen, fallback naar simpele link
Image links: Geautomatiseerde link checker + handmatige QA
VPS resources: SSG minimaliseert server load