Draft Mode (Entwurfsmodus)

Statisches Rendering ist nützlich, wenn Ihre Seiten Daten von einem Headless CMS abrufen. Es ist jedoch nicht ideal, wenn Sie einen Entwurf in Ihrem Headless CMS schreiben und diesen sofort auf Ihrer Seite anzeigen möchten. In diesem Fall sollten Sie Next.js dazu bringen, diese Seiten zur Laufzeit (request time) statt zur Build-Zeit zu rendern und den Entwurfsinhalt statt des veröffentlichten Inhalts abzurufen. Sie möchten, dass Next.js nur für diesen speziellen Fall auf dynamisches Rendering umschaltet.

Next.js bietet eine Funktion namens Draft Mode (Entwurfsmodus), die dieses Problem löst. Hier finden Sie Anweisungen zur Verwendung.

Schritt 1: Route Handler erstellen und aufrufen

Erstellen Sie zunächst einen Route Handler. Er kann beliebig benannt werden, z.B. app/api/draft/route.ts.

Importieren Sie dann draftMode aus next/headers und rufen Sie die Methode enable() auf.

// Route Handler zur Aktivierung des Draft Mode
import { draftMode } from 'next/headers'

export async function GET(request: Request) {
  draftMode().enable()
  return new Response('Draft mode is enabled')
}
// Route Handler zur Aktivierung des Draft Mode
import { draftMode } from 'next/headers'

export async function GET(request) {
  draftMode().enable()
  return new Response('Draft mode is enabled')
}

Dadurch wird ein Cookie gesetzt, um den Draft Mode zu aktivieren. Nachfolgende Anfragen mit diesem Cookie lösen den Draft Mode aus und ändern das Verhalten für statisch generierte Seiten (mehr dazu später).

Sie können dies manuell testen, indem Sie /api/draft aufrufen und die Entwicklertools Ihres Browsers überprüfen. Beachten Sie den Set-Cookie-Response-Header mit einem Cookie namens __prerender_bypass.

Sicherer Zugriff von Ihrem Headless CMS

In der Praxis sollten Sie diesen Route Handler sicher von Ihrem Headless CMS aus aufrufen. Die genauen Schritte hängen von Ihrem Headless CMS ab, aber hier sind einige allgemeine Schritte:

Diese Schritte setzen voraus, dass Ihr Headless CMS das Setzen von benutzerdefinierten Entwurfs-URLs unterstützt. Falls nicht, können Sie diese Methode dennoch verwenden, um Ihre Entwurfs-URLs zu sichern, müssen die URL jedoch manuell erstellen und aufrufen.

Erstens sollten Sie ein geheimes Token mit einem Token-Generator Ihrer Wahl erstellen. Dieses Geheimnis sollte nur Ihrer Next.js-App und Ihrem Headless CMS bekannt sein. Es verhindert, dass Personen ohne CMS-Zugriff auf Entwurfs-URLs zugreifen können.

Zweitens, wenn Ihr Headless CMS benutzerdefinierte Entwurfs-URLs unterstützt, geben Sie folgende URL als Entwurfs-URL an. Hier wird angenommen, dass Ihr Route Handler unter app/api/draft/route.ts liegt.

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>
  • <your-site> sollte Ihre Bereitstellungsdomain sein.
  • <token> sollte durch Ihr geheimes Token ersetzt werden.
  • <path> sollte der Pfad der Seite sein, die Sie anzeigen möchten. Für /posts/foo verwenden Sie &slug=/posts/foo.

Ihr Headless CMS könnte es ermöglichen, eine Variable in der Entwurfs-URL zu verwenden, sodass <path> dynamisch basierend auf den CMS-Daten gesetzt wird, z.B.: &slug=/posts/{entry.fields.slug}

Schließlich im Route Handler:

  • Überprüfen Sie, ob das Secret übereinstimmt und der slug-Parameter existiert (falls nicht, sollte die Anfrage fehlschlagen).
  • Rufen Sie draftMode.enable() auf, um das Cookie zu setzen.
  • Leiten Sie den Browser dann zum angegebenen slug-Pfad weiter.
// Route Handler mit Secret und Slug
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  // Query-String-Parameter parsen
  const { searchParams } = new URL(request.url)
  const secret = searchParams.get('secret')
  const slug = searchParams.get('slug')

  // Secret und Parameter überprüfen
  // Dieses Secret sollte nur diesem Route Handler und dem CMS bekannt sein
  if (secret !== 'MY_SECRET_TOKEN' || !slug) {
    return new Response('Invalid token', { status: 401 })
  }

  // Headless CMS abfragen, um zu prüfen, ob der Slug existiert
  // getPostBySlug implementiert die erforderliche Abfrage-Logik für das Headless CMS
  const post = await getPostBySlug(slug)

  // Wenn der Slug nicht existiert, wird der Draft Mode nicht aktiviert
  if (!post) {
    return new Response('Invalid slug', { status: 401 })
  }

  // Draft Mode durch Setzen des Cookies aktivieren
  draftMode().enable()

  // Zum Pfad des abgerufenen Posts weiterleiten
  // Nicht zu searchParams.slug, um Open-Redirect-Schwachstellen zu vermeiden
  redirect(post.slug)
}
// Route Handler mit Secret und Slug
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'

export async function GET(request) {
  // Query-String-Parameter parsen
  const { searchParams } = new URL(request.url)
  const secret = searchParams.get('secret')
  const slug = searchParams.get('slug')

  // Secret und Parameter überprüfen
  // Dieses Secret sollte nur diesem Route Handler und dem CMS bekannt sein
  if (secret !== 'MY_SECRET_TOKEN' || !slug) {
    return new Response('Invalid token', { status: 401 })
  }

  // Headless CMS abfragen, um zu prüfen, ob der Slug existiert
  // getPostBySlug implementiert die erforderliche Abfrage-Logik für das Headless CMS
  const post = await getPostBySlug(slug)

  // Wenn der Slug nicht existiert, wird der Draft Mode nicht aktiviert
  if (!post) {
    return new Response('Invalid slug', { status: 401 })
  }

  // Draft Mode durch Setzen des Cookies aktivieren
  draftMode().enable()

  // Zum Pfad des abgerufenen Posts weiterleiten
  // Nicht zu searchParams.slug, um Open-Redirect-Schwachstellen zu vermeiden
  redirect(post.slug)
}

Bei Erfolg wird der Browser mit dem Draft-Mode-Cookie zum gewünschten Pfad weitergeleitet.

Schritt 2: Seite aktualisieren

Der nächste Schritt besteht darin, Ihre Seite so zu aktualisieren, dass sie den Wert von draftMode().isEnabled überprüft.

Wenn Sie eine Seite aufrufen, für die das Cookie gesetzt ist, werden die Daten zur Laufzeit (request time) statt zur Build-Zeit abgerufen.

Außerdem ist der Wert von isEnabled dann true.

// Seite, die Daten abruft
import { draftMode } from 'next/headers'

async function getData() {
  const { isEnabled } = draftMode()

  const url = isEnabled
    ? 'https://draft.example.com'
    : 'https://production.example.com'

  const res = await fetch(url)

  return res.json()
}

export default async function Page() {
  const { title, desc } = await getData()

  return (
    <main>
      <h1>{title}</h1>
      <p>{desc}</p>
    </main>
  )
}
// Seite, die Daten abruft
import { draftMode } from 'next/headers'

async function getData() {
  const { isEnabled } = draftMode()

  const url = isEnabled
    ? 'https://draft.example.com'
    : 'https://production.example.com'

  const res = await fetch(url)

  return res.json()
}

export default async function Page() {
  const { title, desc } = await getData()

  return (
    <main>
      <h1>{title}</h1>
      <p>{desc}</p>
    </main>
  )
}

Das war's! Wenn Sie den Draft-Route-Handler (mit secret und slug) von Ihrem Headless CMS oder manuell aufrufen, sollten Sie nun den Entwurfsinhalt sehen können. Und wenn Sie Ihren Entwurf ohne Veröffentlichung aktualisieren, sollten Sie den Entwurf einsehen können.

Setzen Sie dies als Entwurfs-URL in Ihrem Headless CMS oder rufen Sie sie manuell auf, und Sie sollten den Entwurf sehen können.

Terminal
https://<your-site>/api/draft?secret=<token>&slug=<path>

Weitere Details

Standardmäßig endet die Draft-Mode-Sitzung, wenn der Browser geschlossen wird.

Um das Draft-Mode-Cookie manuell zu löschen, erstellen Sie einen Route Handler, der draftMode().disable() aufruft:

import { draftMode } from 'next/headers'

export async function GET(request: Request) {
  draftMode().disable()
  return new Response('Draft mode is disabled')
}
import { draftMode } from 'next/headers'

export async function GET(request) {
  draftMode().disable()
  return new Response('Draft mode is disabled')
}

Rufen Sie dann /api/disable-draft auf, um den Route Handler auszuführen. Wenn Sie diese Route mit next/link aufrufen, müssen Sie prefetch={false} übergeben, um ein versehentliches Löschen des Cookies beim Prefetch zu verhindern.

Einzigartig pro next build

Bei jedem Aufruf von next build wird ein neuer Bypass-Cookie-Wert generiert.

Dies stellt sicher, dass der Bypass-Cookie nicht erraten werden kann.

Gut zu wissen: Um Draft Mode lokal über HTTP zu testen, muss Ihr Browser Third-Party-Cookies und Local Storage-Zugriff erlauben.