Estudo de Caso: Migrando um Portal Universitário de Drupal para Next.js
Tradução para Português Brasileiro
No início de 2024, uma grande universidade estadual procurou-nos com um problema que se tornou dolorosamente comum no ensino superior: sua instalação do Drupal 7 estava chegando ao fim da vida útil, seu portal de estudantes estava sobrecarregado durante períodos de inscrição, e seu localizador de programas — a ferramenta de conversão mais importante do site — levava 8+ segundos para retornar resultados de busca. Tinham 40.000 estudantes ativos, mais de 200 programas acadêmicos e uma janela de seis meses antes do suporte de segurança do Drupal 7 efetivamente terminar. Sem pressão.
Esta é a história de como migramos tudo para Next.js com um backend de CMS headless, reduzimos os tempos de carregamento de página em 73%, e lançamos no prazo. Vou compartilhar as decisões de arquitetura que tomamos (e as que quase acertamos), o processo real de migração, benchmarks de desempenho e as lições que se aplicam a qualquer migração de CMS em larga escala.
Índice
- O Ponto de Partida: Com O Que Estávamos Trabalhando
- Por Que Next.js (E Por Que Não Drupal 10)
- Decisões de Arquitetura
- O Localizador de Programas: Reconstruindo o Recurso Principal
- Migração do Portal de Estudantes
- Estratégia de Migração de Conteúdo
- A Linha do Tempo de 6 Meses
- Resultados de Desempenho
- Lições Aprendidas
- Perguntas Frequentes

O Ponto de Partida: Com O Que Estávamos Trabalhando
Deixa eu pintar o quadro. A presença digital da universidade foi construída no Drupal 7, originalmente lançada por volta de 2014. Ao longo da última década, havia acumulado:
- ~12.000 nós de conteúdo em programas, cursos, perfis de professores, artigos de notícias e eventos
- 200+ páginas de programas acadêmicos cada uma com relacionamentos de taxonomia complexos (nível de grau, departamento, faculdade, formato de entrega, status de acreditação)
- Um localizador de programas customizado construído como uma busca baseada em Drupal Views com filtros expostos — funcional mas lento
- Um portal de estudantes com acesso autenticado a ferramentas de aconselhamento, auditorias de grau, links de registro e dashboards personalizados
- 47 módulos Drupal customizados, dos quais 19 não eram mais mantidos
- 3 camadas de tema diferentes empilhadas uma sobre a outra de sucessivos redesigns
O site era hospedado em duas máquinas virtuais envelhecidas atrás de um balanceador de carga institucional. Durante o pico de inscrições (agosto e janeiro), o localizador de programas regularmente expirava o tempo limite. O time de marketing havia recorrido a postar uma lista em PDF de programas como backup. Isso diz tudo.
Os Core Web Vitals eram ásperos:
| Métrica | Drupal 7 (Antes) | Alvo |
|---|---|---|
| LCP | 6.2s | < 2.5s |
| FID | 380ms | < 100ms |
| CLS | 0.31 | < 0.1 |
| TTFB | 2.8s | < 0.8s |
| Carregamento do Localizador de Programas | 8.4s | < 1.5s |
O Cenário de Partes Interessadas
Projetos web universitários são únicos desafiadores por causa do número de partes interessadas. Estávamos trabalhando com:
- TI Central — responsável pela integração SSO, conformidade de segurança e hospedagem
- Marketing & Comunicações — possuía a marca, estratégia de conteúdo e análises
- O Escritório do Registrador — possuía dados de programas e o sistema de informações de estudantes (SIS)
- Faculdades & Departamentos Individuais — cada um com seus próprios editores de conteúdo (mais de 80 pessoas com acesso ao CMS)
- Governo Estudantil — que defendeu fortemente o design mobile-first (com razão)
Obter alinhamento de todos esses grupos levou as primeiras três semanas do projeto. Realizamos um design sprint para estabelecer prioridades compartilhadas e não-negociáveis.
Por Que Next.js (E Por Que Não Drupal 10)
A pergunta óbvia: por que não apenas atualizar para Drupal 10? O time de TI da universidade havia realmente começado nesse caminho seis meses antes de nos contatar. Eles abandonaram depois de descobrir que 23 de seus 47 módulos customizados não tinham equivalente em Drupal 10 e precisariam ser completamente reescritos.
O cálculo real ficou assim:
| Fator | Migração Drupal 10 | Reconstrução Next.js |
|---|---|---|
| Linha do tempo estimada | 8-10 meses | 6 meses |
| Reescritas de módulos customizados | 23 módulos | N/A (reconstruído como APIs/componentes) |
| Retreinamento de editores de conteúdo | Moderado (nova interface de admin) | Moderado (novo CMS) |
| Teto de desempenho | Melhoria moderada | Melhoria dramática |
| Flexibilidade de hospedagem | LAMP/similar tradicional | Implantação edge, CDN-first |
| Pool de contratação de desenvolvedores | Encolhendo (especialistas em Drupal) | Crescendo (React/Next.js) |
| Custo de manutenção a longo prazo | ~$180K/ano | ~$95K/ano |
A diferença no custo de manutenção foi o clincher para a administração. Desenvolvedores de Drupal com experiência institucional estavam ficando mais difíceis de encontrar e mais caros de reter. O próprio time de TI da universidade tinha três desenvolvedores React e zero especialistas em Drupal depois que seu desenvolvedor Drupal sênior se aposentou.
Escolhemos Next.js especificamente (em vez de Gatsby, Remix ou Astro) por várias razões:
- Renderização híbrida — páginas de programas poderiam ser geradas estaticamente, enquanto o portal de estudantes precisava de renderização do lado do servidor com autenticação
- Rotas de API — poderíamos construir middleware para a integração SIS sem um serviço backend separado
- Regeneração Estática Incremental (ISR) — dados de programas mudam semanalmente, não por hora. ISR com uma janela de revalidação de 1 hora era perfeita
- O time da universidade conhecia React — eles manteriam isso após a entrega
Se você está pesando opções semelhantes, nossa página de capacidades de desenvolvimento Next.js cobre os detalhes técnicos do que normalmente construímos.
Decisões de Arquitetura
Seleção de CMS Headless
Avaliamos cinco opções de CMS headless contra os requisitos da universidade: 80+ editores de conteúdo, relacionamentos de conteúdo complexos, permissões baseadas em funções e um modelo de preço razoável por assento.
Chegamos a Sanity para este projeto. Os fatores principais:
- Consultas GROQ manipularam os relacionamentos de taxonomia complexos entre programas, departamentos e faculdades muito melhor que GraphQL para este caso de uso
- Colaboração em tempo real — múltiplos editores poderiam trabalhar simultaneamente sem conflitos
- Componentes de entrada customizados — construímos um mapeador de pré-requisitos de programa diretamente no studio
- Preço — o plano Enterprise em ~$949/mês estava bem dentro do orçamento, e o custo por usuário era previsível
A modelagem de conteúdo levou cerca de duas semanas. Definimos 14 tipos de documentos e 8 tipos de referência. O esquema de programa sozinho tinha 34 campos, incluindo dados estruturados para schema.org EducationalOrganization e Course markup.
Para mais sobre nossa abordagem de arquitetura de CMS, veja nossa página de desenvolvimento de CMS headless.
Infraestrutura
Implantamos no Vercel para o frontend Next.js (o plano Enterprise, necessário para conformidade FERPA e requisitos de SSO). As rotas autenticadas do portal de estudantes usam renderização do lado do servidor com gerenciamento de sessão através do CAS (Central Authentication Service) SSO existente da universidade.
O fluxo de dados fica assim:
[Sanity CMS] → [Next.js on Vercel] → [CDN Edge]
↕
[University SIS API]
↕
[CAS SSO / LDAP]
Páginas de programa estático são pré-renderizadas no tempo de compilação e revalidadas a cada hora via ISR. O localizador de programas usa uma combinação de dados pré-buscados (carregados no cliente no tempo de compilação como um índice JSON) e filtragem em tempo real — nenhuma viagem de servidor necessária para operações de busca.
A Camada de API
O sistema de informações de estudantes (Ellucian Banner, se você estiver curioso — é sempre Banner) expôs uma API SOAP. Sim, em 2024. Construímos uma camada de tradução usando rotas de API Next.js que consumiram os endpoints SOAP e exposeram endpoints REST limpos no 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',
},
});
}
Esta camada de tradução foi uma das peças de mais alto valor do projeto. Desacoplou o frontend das particularidades do Banner e deu à universidade uma API limpa que eles poderiam usar para projetos futuros (um aplicativo móvel já estava sendo discutido).

O Localizador de Programas: Reconstruindo o Recurso Principal
O localizador de programas era a página mais importante em todo o site. A análise mostrou que respondeu por 34% de todo o tráfego de busca orgânica e foi o #1 ponto de entrada para estudantes em perspectiva. Acertar isso não era uma opção.
A Abordagem Antiga (E Por Que Era Lenta)
A versão do Drupal usou Views com filtros expostos. Cada mudança de filtro disparava uma viagem de servidor completa, consultava novamente o banco de dados e renderizava a página inteira. Com 200+ programas e 6 dimensões de taxonomia (nível de grau, faculdade, departamento, formato de entrega, área de interesse e busca de palavra-chave), a consulta era cara.
A Nova Abordagem
Pré-construímos um índice de busca no tempo de compilação. Todos os 200+ programas foram serializados em um arquivo JSON de ~180KB (~22KB comprimido com gzip) que é enviado com a página. A filtragem acontece inteiramente no cliente usando um hook customizado:
// 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 };
}
Usamos Fuse.js para busca de texto fuzzy e filtragem JavaScript simples para facetas. O resultado: resultados de busca aparecem em menos de 50ms. Sem spinners de carregamento. Sem chamadas de servidor. Os usuários podem bater nos filtros tão rápido quanto quiserem.
Cada resultado de programa vincula a uma página de detalhe gerada estaticamente com markup schema.org completo, que melhorou dramaticamente a aparência da universidade nos recursos de busca relacionados à educação do Google.
Migração do Portal de Estudantes
O portal de estudantes foi a parte mais complicada. Precisava de autenticação, personalização e dados em tempo real do Banner. Não poderíamos gerar estaticamente nenhum dela.
Fluxo de Autenticação
A universidade usa CAS para autenticação única em todos os sistemas institucionais. Integramos CAS com Next.js usando um fluxo de autenticação customizado:
- Usuário não autenticado acessa
/portal→ redirecionado para login do CAS - CAS redireciona de volta com um ticket de serviço
- Nossa rota de API valida o ticket contra o servidor CAS
- Criamos um JWT assinado armazenado em um cookie httpOnly
- Solicitações subsequentes usam o JWT para gerenciamento de sessão
Usamos next-auth (agora Auth.js) com um provedor CAS customizado que escrevemos do zero, já que nenhum provedor CAS mantido existia na época.
Recursos do Portal
O portal de estudantes incluiu:
- Dashboard personalizado com datas de registro próximas, holds e informações do orientador
- Resumo de auditoria de grau puxado do Banner em tempo real
- Links rápidos para o LMS (Canvas), email e sistemas de biblioteca
- Recursos específicos do programa baseados no major declarado do estudante
Todas as páginas do portal usam renderização do lado do servidor. Armazenamos em cache as respostas da API do Banner agressivamente (TTL de 30 segundos para a maioria dos endpoints, TTL de 5 minutos para auditorias de grau) para evitar sobrecarregar seu sistema.
Estratégia de Migração de Conteúdo
Migrar 12.000 nós de conteúdo do Drupal para Sanity exigiu uma abordagem sistemática. Construímos um pipeline de migração customizado:
# Pipeline de migração simplificado
1. Exportar nós Drupal → JSON via comandos Drush customizados
2. Transformar JSON → formato de documento Sanity via scripts Node.js
3. Processar arquivos de mídia → fazer upload para CDN Sanity
4. Importar documentos → API de Migração Sanity
5. Validar → verificações automáticas para referências quebradas
A migração de mídia foi a parte mais tediosa. O gerenciamento de arquivos do Drupal armazena arquivos com caminhos internos e referências de banco de dados. Escrevemos um script que:
- Baixava todo arquivo do diretório de arquivos Drupal
- Fazia upload para o pipeline de assets do Sanity
- Mapeava IDs antigos de arquivo Drupal para novas referências de asset do Sanity
- Atualizava todo conteúdo de rich text para apontar para as novas referências de asset
Este script rodou por cerca de 14 horas no dataset completo. Rodamos três vezes durante o projeto: uma vez para testes iniciais, uma vez no ponto meio para atualizar staging e uma vez para a transição final.
Estratégia de Congelamento de Conteúdo
Implementamos um congelamento de conteúdo em duas fases:
- Semanas 1-20: Editores de conteúdo trabalham no Drupal normalmente. Migramos snapshots para staging semanalmente.
- Semanas 21-23: Entrada dupla. Conteúdo novo vai tanto para Drupal quanto para Sanity. Editores treinados no novo CMS.
- Semana 24: Transição completa. Drupal fica somente leitura, depois offline.
O período de entrada dupla foi doloroso mas necessário. Tínhamos 80+ editores, e eles precisavam desenvolver memória muscular com Sanity antes de ser sua única opção.
A Linha do Tempo de 6 Meses
| Mês | Fase | Entregas Principais |
|---|---|---|
| Mês 1 | Descoberta & Arquitetura | Alinhamento de partes interessadas, seleção de CMS, configuração de infraestrutura, modelagem de conteúdo |
| Mês 2 | Desenvolvimento Principal | Sistema de design, templates de página, páginas de detalhes de programas, navegação |
| Mês 3 | Localizador & Busca de Programas | Índice de busca, UI de filtragem, pipeline de dados de programas, markup de SEO |
| Mês 4 | Portal de Estudantes | Integração CAS, camada de API Banner, dashboard, exibição de auditoria de grau |
| Mês 5 | Migração de Conteúdo & Treinamento | Scripts de migração, treinamento de editor (6 sessões), QA de staging |
| Mês 6 | QA, Desempenho, Lançamento | Testes de carga, auditoria de acessibilidade, congelamento de conteúdo, transição de DNS |
Nosso time era 4 desenvolvedores, 1 designer e 1 gerente de projeto. A universidade forneceu um dono de produto dedicado mais um liaison de TI para o trabalho de integração Banner/CAS.
Enfrentamos dois problemas principais:
Mês 3: A API SOAP do Banner tinha um limite de taxa não documentado de 100 requisições/minuto. Nosso localizador de programas foi projetado para fazer fetch em lote de todos os dados de programa durante a compilação. Precisamos implementar um sistema de fila e distribuir a compilação em múltiplos lotes.
Mês 5: A auditoria de acessibilidade encontrou 34 violações WCAG 2.1 AA. A maioria era herdada do design (contraste de cor insuficiente em botões secundários, indicadores de foco ausentes nos filtros do localizador de programas). Gastamos 8 dias não planejados em remediação.
Resultados de Desempenho
Aqui está o que os números ficaram após o lançamento:
| Métrica | Drupal 7 (Antes) | Next.js (Depois) | Melhoria |
|---|---|---|---|
| LCP | 6.2s | 1.1s | 82% mais rápido |
| FID / INP | 380ms | 45ms | 88% mais rápido |
| CLS | 0.31 | 0.02 | 94% melhor |
| TTFB | 2.8s | 0.12s | 96% mais rápido |
| Carregamento do Localizador de Programas | 8.4s | 0.8s | 90% mais rápido |
| Pontuação Lighthouse | 34 | 97 | +63 pontos |
| Tempo de compilação (completo) | N/A | 4m 12s | — |
| Custo de hospedagem mensal | ~$2.400 | ~$1.100 | 54% menor |
Mas os números que importavam mais para a universidade eram estes:
- O uso do localizador de programas aumentou 156% no primeiro semestre após o lançamento
- A taxa de rejeição móvel caiu de 67% para 31%
- O tráfego de busca orgânica para páginas de programas aumentou 43% em 4 meses (markup schema.org + melhorias de Core Web Vitals)
- Tickets de suporte relacionados ao portal caíram 62% — em grande medida porque as páginas realmente carregavam de forma confiável
- Zero tempo de inatividade durante inscrição de outono — a primeira vez em três anos
Lições Aprendidas
1. Comece a Integração CAS/SSO Cedo
Agendamos a integração CAS para Mês 4. Deveríamos ter começado uma prova de conceito no Mês 1. Times de TI universitários se movem deliberadamente (leia: lentamente) através de revisões de segurança. Obter aprovação da arquitetura SSO levou três semanas de ida e volta com seu escritório de segurança.
2. Modelagem de Conteúdo É Arquitetura
Gastamos duas semanas completas em modelagem de conteúdo antes de escrever qualquer código frontend. Isso pareecia lento na época. Foi o investimento único melhor que fizemos. Quando você tem 200+ programas com relacionamentos complexos entre departamentos, faculdades, níveis de grau, concentrações e formatos de entrega, acertar o schema antecipadamente economiza centenas de horas de refatoração.
3. Treine Editores Cedo, Não Apenas Antes do Lançamento
Inicialmente planejamos treinamento de editores para Mês 5. Movemos para Mês 4 após feedback do dono do produto. Isso deu aos editores seis semanas para se acostumar com Sanity em vez de duas. A qualidade do conteúdo inserido durante o período de entrada dupla foi dramaticamente melhor por causa disso.
4. Banner É Banner
Se você está trabalhando com Ellucian Banner (e se você está no ensino superior, você provavelmente está), orçamente tempo extra para a integração de API. A documentação é escassa, os endpoints SOAP são inconsistentes, e cada instituição customizou sua instância Banner diferentemente. Nossa camada de tradução era essencial.
5. Orçamento para Acessibilidade Desde o Dia Um
Nossas 34 violações WCAG no Mês 5 eram quase inteiramente preveníveis. Agora rodamos verificações axe-core em nosso pipeline de CI em cada pull request. Se você está construindo para uma universidade pública, a conformidade WCAG 2.1 AA não é opcional — é um requisito legal sob a Seção 508.
Se você está enfrentando um desafio de migração semelhante, estamos felizes em conversar sobre os detalhes. Você pode entrar em contato conosco diretamente ou verificar nossa página de preços para como tipicamente escopo esses projetos.
Perguntas Frequentes
Quanto tempo leva para migrar um site de universidade de Drupal para Next.js? Para um site dessa escala — 12.000 nós de conteúdo, 200+ programas, portal de estudantes autenticado — seis meses é realista com um time dedicado de 4-6 pessoas. Sites institucionais menores (sob 2.000 páginas, sem portal) frequentemente podem ser feitos em 3-4 meses. A linha do tempo é dirigida menos pela construção do frontend e mais pela migração de conteúdo, alinhamento de partes interessadas e integração com sistemas institucionais como Banner ou PeopleSoft.
Qual CMS headless é melhor para sites de universidades? Depende do tamanho e conforto técnico do seu time editorial. Escolhemos Sanity para este projeto por causa de sua colaboração em tempo real, modelagem de conteúdo flexível e linguagem de consulta GROQ. Contentful e Storyblok são também opções fortes. Para universidades com times de conteúdo muito grandes (100+ editores), o modelo de fluxo de trabalho e permissões do Contentful podem ser vantajosos. Para times menores que querem mais customização, Sanity tende a vencer.
Next.js pode lidar com portais de estudantes autenticados? Absolutamente. Next.js suporta renderização do lado do servidor para páginas autenticadas, e os componentes de servidor do App Router tornam direto buscar dados específicos do usuário sem expô-los ao bundle do cliente. Integramos com CAS (Central Authentication Service) usando Auth.js com um provedor customizado. O portal lidou com 40.000 estudantes sem problemas de desempenho.
Quanto custa uma migração de Drupal para Next.js para uma universidade? Um projeto dessa escala — localizador de programas, portal de estudantes, 200+ programas, migração de conteúdo completa, configuração de CMS e treinamento — tipicamente varia de $250.000 a $450.000 dependendo da complexidade. No entanto, as economias de longo prazo são significativas. Esta universidade reduziu seus custos anuais de manutenção de aproximadamente $180K para $95K, significando que o projeto se paga em 3-4 anos mesmo no extremo superior do intervalo de orçamento.
O que acontece com SEO durante uma migração de CMS em larga escala? Esta é uma preocupação legítima. Implementamos um mapa de redirecionamento abrangente (mais de 2.400 redirecionamentos 301), preservamos todas as estruturas de URL existentes quando possível, e adicionamos dados estruturados schema.org que o site Drupal não tinha. O tráfego orgânico caiu cerca de 8% nas duas primeiras semanas pós-lançamento (normal para qualquer grande migração), depois se recuperou e excedeu o baseline em 43% dentro de quatro meses.
Drupal 10 é uma escolha melhor que ir headless para universidades? Pode ser, dependendo da situação. Se seu time tem forte experiência em Drupal, seus módulos customizados têm compatibilidade com Drupal 10, e você não precisa das características de desempenho de um site estático/híbrido, Drupal 10 é um caminho perfeitamente válido. Em nosso caso, a universidade perdeu sua experiência em Drupal, tinha 23 módulos incompatíveis, e precisava de melhorias dramáticas de desempenho. A abordagem headless era claramente o ajuste melhor.
Como você lida com migração de conteúdo de Drupal para um CMS headless? Usamos scripts Node.js customizados que exportam conteúdo Drupal via comandos Drush, transformam os dados para bater com o novo esquema de CMS, lidam com migração de arquivo de mídia, e importam tudo via API de migração do CMS. O processo tipicamente roda 3 vezes: uma vez para testes iniciais, uma vez para atualizar staging, e uma vez para transição final. Conteúdo de rich text com mídia incorporada é a parte mais difícil — você precisa remapear cada referência de arquivo interna.
Você pode rodar Drupal e Next.js simultaneamente durante a migração? Sim, e nós recomendamos. Durante nossa migração, Drupal continuou servindo o site de produção enquanto construímos e testamos a versão Next.js em um domínio de staging. Usamos um período de entrada dupla de três semanas onde conteúdo ia para ambos os sistemas. A transição final foi uma troca de DNS que levou cerca de 15 minutos, com Drupal mantido em modo somente leitura por 30 dias como fallback.