Как предварительно просматривать контент с помощью Draft Mode в Next.js

В документации по Pages и документации по получению данных мы рассказывали о том, как предварительно отрендерить страницу во время сборки (Статическая генерация) с помощью getStaticProps и getStaticPaths.

Статическая генерация полезна, когда ваши страницы получают данные из headless CMS. Однако это не идеально, когда вы пишете черновик в вашей headless CMS и хотите сразу просмотреть его на странице. В этом случае вам нужно, чтобы Next.js рендерил эти страницы во время запроса, а не во время сборки, и получал черновик контента вместо опубликованного. Вам нужно, чтобы Next.js обходил статическую генерацию только для этого конкретного случая.

В Next.js есть функция под названием Draft Mode, которая решает эту проблему. Вот инструкции по её использованию.

Шаг 1: Создание и доступ к API-маршруту

Сначала ознакомьтесь с документацией по API-маршрутам, если вы не знакомы с API-маршрутами Next.js.

Сначала создайте API-маршрут. Он может иметь любое имя, например pages/api/draft.ts.

В этом API-маршруте вам нужно вызвать setDraftMode для объекта ответа.

export default function handler(req, res) {
  // ...
  res.setDraftMode({ enable: true })
  // ...
}

Это установит куки для включения режима черновика. Последующие запросы, содержащие этот куки, будут активировать Draft Mode, изменяя поведение для статически сгенерированных страниц (подробнее об этом позже).

Вы можете проверить это вручную, создав API-маршрут, как показано ниже, и обратившись к нему вручную из браузера:

pages/api/draft.ts
// Простой пример для ручного тестирования из браузера.
export default function handler(req, res) {
  res.setDraftMode({ enable: true })
  res.end('Режим черновика включен')
}

Если вы откроете инструменты разработчика в браузере и посетите /api/draft, вы заметите заголовок ответа Set-Cookie с куки под названием __prerender_bypass.

Безопасный доступ из Headless CMS

На практике вам нужно вызывать этот API-маршрут безопасно из вашей headless CMS. Конкретные шаги будут зависеть от используемой headless CMS, но вот некоторые общие действия.

Эти шаги предполагают, что ваша headless CMS поддерживает настройку пользовательских URL черновиков. Если нет, вы всё равно можете использовать этот метод для защиты URL черновиков, но вам нужно будет создавать и обращаться к ним вручную.

Во-первых, создайте секретный токен с помощью генератора токенов. Этот секрет будет известен только вашему приложению Next.js и headless CMS. Это предотвращает доступ к черновикам для тех, у кого нет доступа к CMS.

Во-вторых, если ваша headless CMS поддерживает настройку пользовательских URL черновиков, укажите следующий URL в качестве черновика. Предполагается, что ваш API-маршрут для черновиков находится по адресу pages/api/draft.ts.

Терминал
https://<ваш-сайт>/api/draft?secret=<токен>&slug=<путь>
  • <ваш-сайт> — это домен вашего развёртывания.
  • <токен> — это секретный токен, который вы сгенерировали.
  • <путь> — это путь к странице, которую вы хотите просмотреть. Например, если вы хотите просмотреть /posts/foo, используйте &slug=/posts/foo.

Ваша headless CMS может позволить вам включить переменную в URL черновика, чтобы <путь> мог динамически устанавливаться на основе данных CMS, например: &slug=/posts/{entry.fields.slug}

Наконец, в API-маршруте черновика:

  • Проверьте, совпадает ли секрет и существует ли параметр slug (если нет, запрос должен завершиться ошибкой).
  • Вызовите res.setDraftMode.
  • Затем перенаправьте браузер на путь, указанный в slug. (В следующем примере используется 307 редирект).
export default async (req, res) => {
  // Проверка секрета и параметров
  // Этот секрет должен быть известен только этому API-маршруту и CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Неверный токен' })
  }

  // Запрос к headless CMS для проверки существования указанного `slug`
  // getPostBySlug реализует необходимую логику запроса к headless CMS
  const post = await getPostBySlug(req.query.slug)

  // Если slug не существует, предотвращаем включение режима черновика
  if (!post) {
    return res.status(401).json({ message: 'Неверный slug' })
  }

  // Включаем Draft Mode, устанавливая куки
  res.setDraftMode({ enable: true })

  // Перенаправляем на путь из полученного поста
  // Мы не перенаправляем на req.query.slug, чтобы избежать уязвимостей открытого перенаправления
  res.redirect(post.slug)
}

Если всё прошло успешно, браузер будет перенаправлен на путь, который вы хотите просмотреть, с куки режима черновика.

Шаг 2: Обновление getStaticProps

Следующий шаг — обновить getStaticProps для поддержки режима черновика.

Если вы запрашиваете страницу, которая имеет getStaticProps, с установленным куки (через res.setDraftMode), то getStaticProps будет вызван во время запроса (а не во время сборки).

Кроме того, он будет вызван с объектом context, где context.draftMode будет true.

export async function getStaticProps(context) {
  if (context.draftMode) {
    // динамические данные
  }
}

Мы использовали res.setDraftMode в API-маршруте черновика, поэтому context.draftMode будет true.

Если вы также используете getStaticPaths, то context.params также будет доступен.

Получение данных черновика

Вы можете обновить getStaticProps для получения разных данных в зависимости от context.draftMode.

Например, ваша headless CMS может иметь другой API-эндпоинт для черновиков. В этом случае вы можете изменить URL API-эндпоинта, как показано ниже:

export async function getStaticProps(context) {
  const url = context.draftMode
    ? 'https://draft.example.com'
    : 'https://production.example.com'
  const res = await fetch(url)
  // ...
}

Вот и всё! Если вы обращаетесь к API-маршруту черновика (с secret и slug) из вашей headless CMS или вручную, вы теперь должны видеть черновик контента. И если вы обновите черновик без публикации, вы сможете просмотреть изменения.

Установите это как URL черновика в вашей headless CMS или обращайтесь вручную, и вы сможете видеть черновик.

Терминал
https://<ваш-сайт>/api/draft?secret=<токен>&slug=<путь>

Дополнительные детали

Очистка куки Draft Mode

По умолчанию сессия Draft Mode завершается при закрытии браузера.

Чтобы очистить куки Draft Mode вручную, создайте API-маршрут, который вызывает setDraftMode({ enable: false }):

pages/api/disable-draft.ts
export default function handler(req, res) {
  res.setDraftMode({ enable: false })
}

Затем отправьте запрос на /api/disable-draft для вызова API-маршрута. Если вы вызываете этот маршрут с помощью next/link, вы должны передать prefetch={false}, чтобы случайно не удалить куки при предзагрузке.

Совместимость с getServerSideProps

Draft Mode работает с getServerSideProps и доступен как ключ draftMode в объекте context.

Полезно знать: Не следует устанавливать заголовок Cache-Control при использовании Draft Mode, так как его нельзя обойти. Вместо этого мы рекомендуем использовать ISR.

Совместимость с API-маршрутами

API-маршруты будут иметь доступ к draftMode в объекте запроса. Например:

export default function myApiRoute(req, res) {
  if (req.draftMode) {
    // получение данных черновика
  }
}

Уникальность для каждого next build

Новое значение куки для обхода будет генерироваться каждый раз при запуске next build.

Это гарантирует, что куки для обхода нельзя угадать.

Полезно знать: Для тестирования Draft Mode локально через HTTP ваш браузер должен разрешать сторонние куки и доступ к локальному хранилищу.