16 de sept de 2025
Revalidación on-demand con Next.js y Directus
Una guía para usar una de las herramientas más potentes de Next.js hoy, la revalidación on-demand, y conectarla con Directus.
Introducción
La revalidación on-demand es una funcionalidad muy potente que nos permite actualizar dinámicamente contenido estático en aplicaciones Next.js. Esta técnica acorta la brecha entre la generación estática (SSG) y el renderizado del lado del servidor (SSR), ofreciendo lo mejor de ambos mundos.
En conjunto, estas herramientas permiten un sistema de actualización de contenido flexible, performante y fácil de administrar. Este artículo te guía en la configuración de la revalidación on-demand para que tu app de Next.js sirva siempre el contenido más actualizado de tu CMS Directus.
Para esta guía asumo que ya tenés tu proyecto de Next.js configurado con App Router y tu instancia de Directus en funcionamiento.
Configurando el entorno
Empecemos por crear el archivo .env en la base del proyecto y agregando la key REVALIDATION_SECRET con un valor secreto.
REVALIDATION_SECRET="[TU_SECRETO]"Configurando la ruta API de revalidación en Next.js
Ahora creemos la ruta API que va a manejar las solicitudes de revalidación.
Crea un archivo nuevo en: app/api/revalidate_tag/route.ts y pegá este script adentro:
import { revalidateTag } from 'next/cache';
import type { NextRequest } from 'next/server';
enum Error {
InvalidSecret = 'invalid-secret',
InvalidTag = 'invalid-tag',
}
const errorMessages = {
[Error.InvalidSecret]: 'Secreto inválido',
[Error.InvalidTag]: 'Tag inválido',
};
export async function POST(request: NextRequest) {
// data contiene el body de la solicitud (lo vamos a enviar desde el Flow de Directus).
const data = await request.json();
// verificamos que el secreto recibido coincida con el de nuestra app
const isSecretValid = data.secret === process.env.REVALIDATION_SECRET;
// verificamos que llegue un tag
const isTagValid = Boolean(data.tag);
function getErrorMessage() {
if (!isSecretValid) return errorMessages[Error.InvalidSecret];
if (!isTagValid) return errorMessages[Error.InvalidTag];
}
const errorMessage = getErrorMessage();
// Si hay un error, no revalidamos
if (errorMessage) {
return Response.json({
revalidated: false,
now: Date.now(),
message: errorMessage,
});
}
// Acá ocurre la magia: con esto le decimos a Next
// que purgue toda la caché asociada a este tag
revalidateTag(data.tag);
return Response.json({ revalidated: true, now: Date.now() });
}Configurando los tags de las solicitudes en Next.js
Ahora vamos a preparar los tags para nuestras requests, de esta manera Next sabra que purgar cuando se use validateTags.
Con fetch
Si hacés solicitudes con fetch a secas, alcanza con configurarlo en el segundo parámetro de fetch (RequestInit):
fetch('my-request', { next: { tags: ['my-tag'] } });Con wrappers de fetch personalizados
En mi caso, uso el SDK de Directus (@directus/sdk) para las requests, lo que significa que tengo que envolver las request con unstable_cache de Next. Voy a hacer esto en una pagina de un articulo de mi blog: app/blog/[slug]/page.tsx.
Tengo una request simple para levantar un post desde mi collection posts via slug asi:
import { readItem } from '@directus/sdk';
import directus from '../client/directus';
export type PostData = {
slug: string;
title: string;
shortDescription?: string;
content: string;
};
export async function getPost(
slug: string,
version?: string
): Promise<PostData> {
return directus.request<PostData>(
readItem('posts', slug, {
version,
fields: [
'slug',
'title',
'shortDescription',
'content',
],
})
);
}Y en la página la uso así:
type PageProps = {
params: {
slug: string;
};
};
export default async function PostPage({ params }: PageProps) {
const post = await getPost(params.slug);
...
}Asi que ahora lo que hago, es envolver la request en unstable_cache:
// Importamos unstable_cache
import { unstable_cache } from 'next/cache';
type PageProps = {
params: {
slug: string;
};
};
export default async function PostPage({ params }: PageProps) {
// Creamos la solicitud proxy con unstable_cache y el tag "posts" para este slug.
const getCachedPost = unstable_cache(getPost, [params.slug], {
tags: ['posts'],
});
const post = await getCachedPost(params.slug);
...
}Eso es prácticamente todo del lado de Next.js; ¡sigamos con la configuración de Directus!
Configurando el Flow de Directus
En el panel de Directus, andá a Settings -> Flows -> Create Flow (arriba a la derecha).

En Flow Setup, elegí un nombre descriptivo para el flow. Yo lo voy a llamar "Revalidate Posts"

Ahora, en Trigger Setup:
- Elegí Event Hook.
- En Type, seleccioná Action (non-blocking).
- In Scope, set cuándo you want to trigger the flow. In my case I will be doing it on
items.create,items.updateyitems.delete - In Collections, select the collection/s that should be targeted. In my case this will be
Posts.

Ahora presiona en save y deberíamos estar dentro del dashboard de Flow. Ya deberiamos estar en el ultimo paso de esta guía (finalmente).
Creemos una nueva operación presionando el símbolo + a la derecha del nuevo trigger creado. En la modal de operación, elegí un nombre descriptivo para la operación. Yo le voy a poner "Revalidation request".
- Selecciona
Webhook / Request URL. - En Method, selecciona
POST - En URL, apuntá a la URL de tu API de Next.js, por ejemplo "/api/revalidate_tag"
- En Request Body agregá lo siguiente (recordá que podés cambiar tag por el tag que hayas configurado).
{
"secret": "[TU_SECRETO_DE_REVALIDACION]"
"tag": "posts"
}
¡Eso es todo! Ahora probemos que funcione como esperamos.
Poniéndolo a prueba
Como el servidor de desarrollo no tiene caché, necesitamos generar un build y levantar el servidor de producción:
npm run build && npm run startAhora andá a tu panel de Directus y creá, borrá o actualizá un post (o el ítem que corresponda a la colección que apuntás), recargá tu página de Next.js y los cambios deberían verse casi al instante.
