Migrer un portail universitaire de Drupal vers Next.js

Au début de 2024, une grande université publique nous a contactés avec un problème qui est devenu douloureusement courant dans l'enseignement supérieur : leur installation Drupal 7 arrivait en fin de vie, leur portail étudiant s'effondrait sous la charge pendant les périodes d'inscription, et leur outil de recherche de programmes — le plus important outil de conversion de leur site web — prenait plus de 8 secondes pour retourner des résultats de recherche. Ils avaient 40 000 étudiants actifs, plus de 200 programmes académiques, et une fenêtre de six mois avant la fin du support de sécurité de Drupal 7. Pas de pression.

C'est l'histoire de la façon dont nous avons migré l'ensemble du système vers Next.js avec un backend CMS sans tête, avons réduit les temps de chargement des pages de 73 %, et l'avons livré à temps. Je vais partager les décisions architecturales que nous avons prises (et celles que nous aurions presque mal prises), le processus de migration réel, les repères de performance, et les leçons qui s'appliquent à toute migration CMS à grande échelle.

Table des matières

Étude de cas : migration d'un portail universitaire de Drupal vers Next.js

Le point de départ : ce avec quoi nous avons travaillé

Laissez-moi peindre le tableau. La présence numérique de l'université a été construite sur Drupal 7, initialement lancée vers 2014. Au cours de la dernière décennie, elle avait accumulé :

  • ~12 000 nœuds de contenu répartis sur des programmes, des cours, des profils de faculté, des articles d'actualité et des événements
  • 200+ pages de programmes académiques chacune avec des relations de taxonomie complexes (niveau de diplôme, département, collège, format de livraison, statut d'accréditation)
  • Un outil de recherche de programmes personnalisé construit comme une recherche basée sur les vues Drupal avec des filtres exposés — fonctionnel mais lent
  • Un portail étudiant avec accès authentifié aux outils d'orientation, aux audits de diplôme, aux liens d'inscription et aux tableaux de bord personnalisés
  • 47 modules Drupal personnalisés, dont 19 n'étaient plus maintenus
  • 3 couches de thème différentes empilées les unes sur les autres à partir des redesigns successifs

Le site était hébergé sur deux machines virtuelles vieillissantes derrière un équilibreur de charge institutionnel. Pendant les inscriptions de pointe (août et janvier), l'outil de recherche de programmes s'expirerait régulièrement. L'équipe marketing avait eu recours à l'affichage d'une liste PDF de programmes en secours. Cela vous dit tout.

Les Core Web Vitals étaient mauvaises :

Métrique Drupal 7 (avant) Cible
LCP 6.2s < 2.5s
FID 380ms < 100ms
CLS 0.31 < 0.1
TTFB 2.8s < 0.8s
Chargement de l'outil de recherche 8.4s < 1.5s

Le paysage des parties prenantes

Les projets web universitaires sont exceptionnellement difficiles en raison du nombre de parties prenantes. Nous avons travaillé avec :

  • IT central — responsable de l'intégration SSO, de la conformité de sécurité et de l'hébergement
  • Marketing et communications — propriétaire de la marque, de la stratégie de contenu et de l'analytique
  • Le bureau du registraire — propriétaire des données de programme et du système d'information étudiant (SIS)
  • Collèges et départements individuels — chacun avec ses propres éditeurs de contenu (plus de 80 personnes avec accès CMS)
  • Gouvernement étudiant — qui a plaidé fortement pour un design mobile-first (à juste titre)

Obtenir l'alignement de tous ces groupes a pris les trois premières semaines du projet. Nous avons mené un sprint de conception pour établir des priorités partagées et des points non négociables.

Pourquoi Next.js (et pourquoi pas Drupal 10)

La question évidente : pourquoi ne pas simplement mettre à niveau vers Drupal 10 ? L'équipe IT de l'université avait en fait commencé sur cette voie six mois avant de nous contacter. Ils l'avaient abandonnée après avoir découvert que 23 de leurs 47 modules personnalisés n'avaient pas d'équivalent Drupal 10 et devraient être complètement réécrits.

Le calcul réel ressemblait à ceci :

Facteur Migration Drupal 10 Reconstruction Next.js
Chronologie estimée 8-10 mois 6 mois
Réécriture des modules personnalisés 23 modules S/O (reconstruits en tant qu'API/composants)
Reconversion des éditeurs de contenu Modérée (nouvelle UI admin) Modérée (nouveau CMS)
Plafond de performance Amélioration modérée Amélioration dramatique
Flexibilité d'hébergement LAMP traditionnel/similaire Déploiement edge, CDN-first
Bassin de recrutement de développeurs En baisse (spécialistes Drupal) En croissance (React/Next.js)
Coût de maintenance à long terme ~180K$/an ~95K$/an

La différence de coût de maintenance a été déterminante pour l'administration. Les développeurs Drupal ayant une expérience institutionnelle devenaient plus difficiles à trouver et plus coûteux à retenir. L'équipe IT propre de l'université avait trois développeurs React et zéro spécialistes Drupal après le départ à la retraite de leur senior développeur Drupal.

Nous avons choisi Next.js spécifiquement (plutôt que Gatsby, Remix ou Astro) pour plusieurs raisons :

  1. Rendu hybride — les pages de programmes pouvaient être générées statiquement, tandis que le portail étudiant avait besoin du rendu côté serveur avec authentification
  2. Routes API — nous pourrions construire un middleware pour l'intégration du SIS sans un service backend séparé
  3. Régénération statique incrémentale (ISR) — les données des programmes changent hebdomadairement, pas horaire. L'ISR avec une fenêtre de revalidation d'une heure était parfait
  4. L'équipe de l'université connaissait React — ils maintiendraient cela après remise

Si vous pesez des options similaires, notre page de capacités de développement Next.js couvre les détails techniques de ce que nous construisons généralement.

Décisions architecturales

Sélection du CMS sans tête

Nous avons évalué cinq options de CMS sans tête par rapport aux exigences de l'université : 80+ éditeurs de contenu, relations de contenu complexes, autorisations basées sur les rôles, et un modèle de tarification par siège raisonnable.

Nous avons opté pour Sanity pour ce projet. Les facteurs clés :

  • Les requêtes GROQ géraient beaucoup mieux les relations de taxonomie complexes entre les programmes, les départements et les collèges que GraphQL pour ce cas d'usage
  • Collaboration en temps réel — plusieurs éditeurs pourraient travailler simultanément sans conflits
  • Composants d'entrée personnalisés — nous avons construit un mappeur de prérequis de programme directement dans le studio
  • Tarification — le plan Entreprise à ~949$/mois était bien dans le budget, et le coût par utilisateur était prévisible

La modélisation du contenu a pris environ deux semaines. Nous avons défini 14 types de documents et 8 types de références. Le schéma du programme seul avait 34 champs, incluant des données structurées pour le balisage EducationalOrganization et Course de schema.org.

Pour plus sur notre approche de l'architecture CMS, consultez notre page de développement CMS sans tête.

Infrastructure

Nous avons déployé sur Vercel pour le frontend Next.js (le plan Entreprise, requis pour la conformité FERPA et les exigences SSO). Les routes authentifiées du portail étudiant utilisent le rendu côté serveur avec la gestion de session via le CAS (Service central d'authentification) SSO existant de l'université.

Le flux de données ressemble à ceci :

[CMS Sanity] → [Next.js sur Vercel] → [CDN Edge]
                    ↕
           [API SIS universitaire]
                    ↕
           [CAS SSO / LDAP]

Les pages de programme statiques sont pré-rendues au moment de la construction et revalidées toutes les heures via ISR. L'outil de recherche de programmes utilise une combinaison de données pré-extraites (chargées dans le client au moment de la construction en tant qu'index JSON) et de filtrage en temps réel — aucun aller-retour serveur nécessaire pour les opérations de recherche.

La couche API

Le système d'information étudiant (Ellucian Banner, si vous êtes curieux — c'est toujours Banner) exposait une API SOAP. Oui, en 2024. Nous avons construit une couche de traduction en utilisant des routes API Next.js qui consommaient les points de terminaison SOAP et exposaient des points de terminaison REST propres au frontend :

// /app/api/programs/[programId]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { fetchFromBanner } from '@/lib/banner-client';
import { transformProgramData } from '@/lib/transforms';

export async function GET(
  request: NextRequest,
  { params }: { params: { programId: string } }
) {
  const bannerData = await fetchFromBanner(
    'PROGRAM_DETAIL',
    { programCode: params.programId }
  );
  
  const program = transformProgramData(bannerData);
  
  return NextResponse.json(program, {
    headers: {
      'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
    },
  });
}

Cette couche de traduction était l'une des pièces de plus haute valeur du projet. Elle a découplé le frontend des bizarreries de Banner et a donné à l'université une API propre qu'ils pourraient utiliser pour des projets futurs (une application mobile était déjà en discussion).

Étude de cas : migration d'un portail universitaire de Drupal vers Next.js - architecture

L'outil de recherche de programmes : reconstruction de la fonctionnalité clé

L'outil de recherche de programmes était la page la plus importante de tout le site. L'analytique a montré qu'il représentait 34 % de tout le trafic de recherche organique et qu'il était le point d'entrée n°1 pour les futurs étudiants. Se tromper sur ceci n'était pas une option.

L'ancienne approche (et pourquoi c'était lent)

La version Drupal utilisait Views avec des filtres exposés. Chaque changement de filtre déclenchait un aller-retour serveur complet, relançait une requête à la base de données et re-rendait la page entière. Avec 200+ programmes et 6 dimensions de taxonomie (niveau de diplôme, collège, département, format de livraison, domaine d'intérêt et recherche par mot-clé), la requête était coûteuse.

La nouvelle approche

Nous avons pré-construit un index de recherche au moment de la construction. Tous les 200+ programmes ont été sérialisés dans un fichier JSON ~180KB (compressé à ~22KB) qui est livré avec la page. Le filtrage se fait entièrement côté client en utilisant un hook personnalisé :

// hooks/useProgramSearch.ts
import { useMemo, useState } from 'react';
import Fuse from 'fuse.js';
import { Program, ProgramFilters } from '@/types';

const fuseOptions = {
  keys: [
    { name: 'title', weight: 0.4 },
    { name: 'description', weight: 0.2 },
    { name: 'keywords', weight: 0.3 },
    { name: 'department', weight: 0.1 },
  ],
  threshold: 0.3,
};

export function useProgramSearch(programs: Program[]) {
  const [filters, setFilters] = useState<ProgramFilters>({});
  const fuse = useMemo(() => new Fuse(programs, fuseOptions), [programs]);

  const results = useMemo(() => {
    let filtered = programs;

    if (filters.degreeLevel) {
      filtered = filtered.filter(p => p.degreeLevel === filters.degreeLevel);
    }
    if (filters.college) {
      filtered = filtered.filter(p => p.college === filters.college);
    }
    if (filters.deliveryFormat) {
      filtered = filtered.filter(p => 
        p.deliveryFormats.includes(filters.deliveryFormat!)
      );
    }
    if (filters.searchQuery) {
      const fuseResults = fuse.search(filters.searchQuery);
      const fuseIds = new Set(fuseResults.map(r => r.item.id));
      filtered = filtered.filter(p => fuseIds.has(p.id));
    }

    return filtered;
  }, [programs, filters, fuse]);

  return { results, filters, setFilters };
}

Nous avons utilisé Fuse.js pour la recherche de texte floue et le filtrage JavaScript ordinaire pour les facettes. Le résultat : les résultats de recherche s'affichent en moins de 50ms. Pas de spinners de chargement. Pas d'appels serveur. Les utilisateurs peuvent modifier les filtres aussi vite qu'ils le souhaitent.

Chaque résultat de programme renvoie à une page de détail générée statiquement avec le balisage complet de schema.org, ce qui a considérablement amélioré l'apparence de l'université dans les fonctionnalités de recherche liées à l'éducation de Google.

Migration du portail étudiant

Le portail étudiant a été la partie la plus délicate. Il nécessitait l'authentification, la personnalisation et les données en temps réel de Banner. Nous ne pouvions pas générer statiquement quoi que ce soit.

Flux d'authentification

L'université utilise CAS pour l'authentification unique sur tous les systèmes institutionnels. Nous avons intégré CAS à Next.js en utilisant un flux d'authentification personnalisé :

  1. L'utilisateur non authentifié accède à /portal → redirigé vers la connexion CAS
  2. CAS redirige avec un jeton de service
  3. Notre route API valide le jeton auprès du serveur CAS
  4. Nous créons un JWT signé stocké dans un cookie httpOnly
  5. Les requêtes suivantes utilisent le JWT pour la gestion de session

Nous avons utilisé next-auth (maintenant Auth.js) avec un fournisseur CAS personnalisé que nous avons écrit de zéro, car aucun fournisseur CAS maintenu n'existait à l'époque.

Fonctionnalités du portail

Le portail étudiant incluait :

  • Tableau de bord personnalisé avec les dates d'inscription à venir, les blocages et les informations du conseiller
  • Résumé de l'audit de diplôme tiré de Banner en temps réel
  • Liens rapides vers le LMS (Canvas), l'e-mail et les systèmes de bibliothèque
  • Ressources spécifiques au programme en fonction de la spécialisation déclarée de l'étudiant

Toutes les pages du portail utilisent le rendu côté serveur. Nous mettons en cache les réponses de l'API Banner de manière agressive (TTL de 30 secondes pour la plupart des points de terminaison, TTL de 5 minutes pour les audits de diplôme) pour éviter de surcharger leur système.

Stratégie de migration du contenu

La migration de 12 000 nœuds de contenu de Drupal vers Sanity nécessitait une approche systématique. Nous avons construit un pipeline de migration personnalisé :

# Pipeline de migration simplifié
1. Exporter les nœuds Drupal → JSON via des commandes Drush personnalisées
2. Transformer JSON → format de document Sanity via des scripts Node.js
3. Traiter les fichiers multimédias → télécharger vers le CDN Sanity
4. Importer les documents → API de migration Sanity
5. Valider → vérifications automatisées pour les références cassées

La migration médias a été la partie la plus fastidieuse. La gestion des fichiers de Drupal stocke les fichiers avec des chemins internes et des références à la base de données. Nous avons écrit un script qui :

  1. A téléchargé tous les fichiers du répertoire des fichiers Drupal
  2. Les a téléchargés vers le pipeline d'assets de Sanity
  3. A mappé les anciens ID de fichiers Drupal aux nouvelles références d'assets Sanity
  4. A mis à jour tout le contenu de texte riche pour pointer vers les nouvelles références d'assets

Ce script a fonctionné pendant environ 14 heures sur l'ensemble des données. Nous l'avons exécuté trois fois pendant le projet : une fois pour les tests initiaux, une fois au point médian pour rafraîchir la mise en scène, et une fois pour la transition finale.

Stratégie de gel du contenu

Nous avons implémenté un gel du contenu en deux phases :

  • Semaines 1-20 : Les éditeurs de contenu travaillent dans Drupal normalement. Nous migrons les snapshots vers la mise en scène hebdomadairement.
  • Semaines 21-23 : Double entrée. Le nouveau contenu est entré à la fois dans Drupal et Sanity. Les éditeurs sont formés au nouveau CMS.
  • Semaine 24 : Transition complète. Drupal devient en lecture seule, puis hors ligne.

La période de double entrée était douloureuse mais nécessaire. Nous avions 80+ éditeurs, et ils avaient besoin de construire la mémoire musculaire avec Sanity avant que ce soit leur seule option.

La chronologie de 6 mois

Mois Phase Livrables clés
Mois 1 Découverte et architecture Alignement des parties prenantes, sélection du CMS, configuration de l'infrastructure, modélisation du contenu
Mois 2 Développement principal Système de conception, modèles de page, pages de détail du programme, navigation
Mois 3 Outil de recherche et recherche de programmes Index de recherche, filtrage UI, pipeline de données du programme, balisage SEO
Mois 4 Portail étudiant Intégration CAS, couche API Banner, tableau de bord, affichage de l'audit de diplôme
Mois 5 Migration du contenu et formation Scripts de migration, formation des éditeurs (6 sessions), QA de mise en scène
Mois 6 QA, performance, lancement Test de charge, audit d'accessibilité, gel du contenu, transition DNS

Notre équipe était 4 développeurs, 1 concepteur et 1 chef de projet. L'université a fourni un propriétaire de produit dédié plus un agent de liaison IT pour les travaux d'intégration Banner/CAS.

Nous avons rencontré deux gros problèmes :

  1. Mois 3 : L'API SOAP de Banner avait une limite de taux non documentée de 100 requêtes/minute. Notre outil de recherche de programmes a été conçu pour extraire par lots toutes les données du programme pendant la construction. Nous avons dû implémenter un système de mise en file d'attente et étaler la construction sur plusieurs lots.

  2. Mois 5 : L'audit d'accessibilité a trouvé 34 violations WCAG 2.1 AA. La plupart étaient héritées de la conception (contraste de couleur insuffisant sur les boutons secondaires, indicateurs de focus manquants sur les filtres de l'outil de recherche de programmes). Nous avons passé 8 jours imprévus sur la correction.

Résultats de performance

Voici à quoi ressemblaient les chiffres après le lancement :

Métrique Drupal 7 (avant) Next.js (après) Amélioration
LCP 6.2s 1.1s 82% plus rapide
FID / INP 380ms 45ms 88% plus rapide
CLS 0.31 0.02 94% meilleur
TTFB 2.8s 0.12s 96% plus rapide
Chargement de l'outil de recherche 8.4s 0.8s 90% plus rapide
Score Lighthouse 34 97 +63 points
Temps de construction (complète) S/O 4m 12s
Coût d'hébergement mensuel ~2 400$ ~1 100$ 54% moins cher

Mais les chiffres qui importaient le plus à l'université étaient ceux-ci :

  • L'utilisation de l'outil de recherche de programmes a augmenté de 156% au cours du premier semestre après le lancement
  • Le taux de rebond mobile est passé de 67% à 31%
  • Le trafic de recherche organique vers les pages de programme a augmenté de 43% en 4 mois (balisage schema.org + améliorations des Core Web Vitals)
  • Les tickets de support liés au portail ont diminué de 62% — largement parce que les pages se chargeaient réellement de manière fiable
  • Zéro temps d'arrêt pendant l'inscription d'automne — la première fois en trois ans

Leçons apprises

1. Commencez l'intégration CAS/SSO tôt

Nous avons prévu l'intégration CAS pour le mois 4. Nous aurions dû commencer une preuve de concept au mois 1. Les équipes IT universitaires avancent délibérément (lisez : lentement) dans les examens de sécurité. L'obtention de l'approbation de l'architecture SSO a pris trois semaines de va-et-vient avec leur bureau de sécurité.

2. La modélisation du contenu est l'architecture

Nous avons passé deux semaines complètes sur la modélisation du contenu avant d'écrire un code frontend. Cela semblait lent à l'époque. C'était le meilleur investissement que nous avons fait. Quand vous avez 200+ programmes avec des relations complexes entre les départements, les collèges, les niveaux de diplôme, les concentrations et les formats de livraison, la bonne schématisation dès le départ économise des centaines d'heures de refactorisation.

3. Formez les éditeurs tôt, pas seulement avant le lancement

Nous avons initialement prévu la formation des éditeurs pour le mois 5. Nous l'avons déplacée au mois 4 suite aux commentaires du propriétaire du produit. Cela a donné aux éditeurs six semaines pour se familiariser avec Sanity au lieu de deux. La qualité du contenu entré pendant la période de double entrée était dramatiquement meilleure grâce à cela.

4. Banner est Banner

Si vous travaillez avec Ellucian Banner (et si vous êtes dans l'enseignement supérieur, vous l'êtes probablement), budgétez du temps supplémentaire pour l'intégration API. La documentation est clairsemée, les points de terminaison SOAP sont incohérents, et chaque institution a personnalisé sa propre instance Banner différemment. Notre couche de traduction était essentielle.

5. Budgétez l'accessibilité dès le départ

Nos 34 violations WCAG au mois 5 étaient presque entièrement évitables. Nous exécutons maintenant les vérifications axe-core dans notre pipeline CI sur chaque pull request. Si vous construisez pour une université publique, la conformité WCAG 2.1 AA n'est pas optionnelle — c'est une exigence légale en vertu de la section 508.

Si vous faites face à un défi de migration similaire, nous sommes heureux de discuter des détails. Vous pouvez nous contacter directement ou consultez notre page de tarification pour savoir comment nous scopons généralement ces projets.

FAQ

Combien de temps faut-il pour migrer un site web universitaire de Drupal vers Next.js ? Pour un site de cette échelle — 12 000 nœuds de contenu, 200+ programmes, portail étudiant authentifié — six mois est réaliste avec une équipe dédiée de 4-6 personnes. Les sites institutionnels plus petits (moins de 2 000 pages, pas de portail) peuvent souvent être faits en 3-4 mois. La chronologie est moins dirigée par la construction du frontend et plus par la migration du contenu, l'alignement des parties prenantes et l'intégration aux systèmes institutionnels comme Banner ou PeopleSoft.

Quel CMS sans tête est le meilleur pour les sites web d'enseignement supérieur ? Cela dépend de la taille et du confort technique de votre équipe éditoriale. Nous avons choisi Sanity pour ce projet en raison de sa collaboration en temps réel, sa modélisation de contenu flexible et son langage de requête GROQ. Contentful et Storyblok sont également d'excellentes options. Pour les universités avec de très grandes équipes de contenu (100+ éditeurs), le modèle de flux de travail et d'autorisations de Contentful peut être avantageux. Pour les équipes plus petites qui souhaitent plus de personnalisation, Sanity tend à gagner.

Next.js peut-il gérer les portails étudiants authentifiés ? Absolument. Next.js supporte le rendu côté serveur pour les pages authentifiées, et les composants serveur du routeur d'application facilitent l'extraction de données spécifiques à l'utilisateur sans les exposer au bundle client. Nous avons intégré avec CAS (Service central d'authentification) en utilisant Auth.js avec un fournisseur personnalisé. Le portail gérait 40 000 étudiants sans problèmes de performance.

Combien coûte une migration de Drupal vers Next.js pour une université ? Un projet de cette envergure — outil de recherche de programmes, portail étudiant, 200+ programmes, migration de contenu complète, configuration du CMS et formation — s'inscrit généralement entre 250 000$ et 450 000$ selon la complexité. Cependant, les économies à long terme sont significatives. Cette université a réduit ses coûts de maintenance annuels d'environ 180K$ à 95K$, ce qui signifie que le projet se paie en 3-4 ans même à l'extrémité supérieure de la gamme budgétaire.

Que se passe-t-il avec le référencement lors d'une migration CMS à grande échelle ? C'est une préoccupation légitime. Nous avons implémenté une carte de redirection complète (plus de 2 400 redirections 301), préservé toutes les structures d'URL existantes autant que possible, et ajouté des données structurées schema.org que le site Drupal n'avait pas. Le trafic organique a baissé d'environ 8% au cours des deux premières semaines suivant le lancement (normal pour toute migration majeure), puis s'est rétabli et a dépassé la base de 43% dans les quatre mois.

Drupal 10 est-il un meilleur choix que d'aller sans tête pour les universités ? Cela peut être, selon la situation. Si votre équipe a une forte expertise Drupal, vos modules personnalisés ont la compatibilité Drupal 10, et vous n'avez pas besoin des caractéristiques de performance d'un site statique/hybride, Drupal 10 est un chemin parfaitement valide. Dans notre cas, l'université avait perdu son expertise Drupal, avait 23 modules incompatibles, et avait besoin d'améliorations dramatiques de la performance. L'approche sans tête était clairement le meilleur choix.

Comment gérez-vous la migration du contenu de Drupal vers un CMS sans tête ? Nous utilisons des scripts Node.js personnalisés qui exportent le contenu Drupal via des commandes Drush, transforment les données pour correspondre au nouveau schéma CMS, gèrent la migration des fichiers multimédias et importent tout via l'API de migration du CMS. Le processus s'exécute généralement 3 fois : une fois pour les tests initiaux, une fois pour rafraîchir la mise en scène, et une fois pour la transition finale. Le contenu de texte riche avec les médias intégrés est la partie la plus difficile — vous devez remapper chaque référence de fichier interne.

Pouvez-vous exécuter Drupal et Next.js simultanément lors d'une migration ? Oui, et nous le recommandons. Pendant notre migration, Drupal continuait à servir le site de production tandis que nous construisions et testions la version Next.js sur un domaine de mise en scène. Nous avons utilisé une période de double entrée de trois semaines où le contenu allait dans les deux systèmes. La transition finale a été un changement DNS qui a pris environ 15 minutes, avec Drupal maintenu en mode lecture seule pendant 30 jours comme secours.