Todos los artículos

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.

Miniatura de Next.js y 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.

bash
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:

ts
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):

ts
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:

ts
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í:

ts
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:

ts
// 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).

Panel de flujos de Directus

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

Flujos de Directus, panel de configuración de un flujo nuevo

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.update y items.delete
  • In Collections, select the collection/s that should be targeted. In my case this will be Posts.
Flujos de Directus, panel de configuración del disparador

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).
bash
{
"secret": "[TU_SECRETO_DE_REVALIDACION]"
"tag": "posts"
}
Flujos de Directus, panel de edición de la operación

¡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:

bash
npm run build && npm run start

Ahora 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.