SSR-дружественный клиент GraphQL

В предыдущей статье мы сгенерировали типобезопасные GraphQL-композиции из файлов .graphql для использования в нашем коде фронтенда VueJS. Под капотом сгенерированные функции полагаются на клиент @urql/vue, который необходимо сначала настроить, прежде чем пытаться получить доступ к нашим операциям GraphQL.

Настройка @urql/vue с помощью плагина Nuxt3

Для начала мы можем предоставить базовый клиент @urql/vue с плагином в plugins/urql.ts:

import urql from "@urql/vue";
import { defineNuxtPlugin, useRuntimeConfig } from "#app";

export default defineNuxtPlugin((nuxtApp) => {
  const { graphqlApiURL } = useRuntimeConfig();

  nuxtApp.vueApp.use(urql, {
    url: graphqlApiURL,
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Мы предоставляем graphqlApiURL в качестве публичной конфигурации времени выполнения в nuxt.config.ts:

publicRuntimeConfig: {
  graphqlApiURL: process.env.GRAPHQL_API_URL || "http://localhost:3000/api/graphql",
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы могли бы попытаться определить URL API на основе опций Nuxt во время сборки или выполнения, но гораздо проще определить его явно.

Настройка обмена SSR

При выполнении запросов в странице и/или компоненте(ах), полученные результаты могут быть объединены и сериализованы в полезной нагрузке страницы, отправленной сервером, а затем повторно обработаны клиентом при рендеринге.

Для этого нам нужно переопределить стандартные обмены urql, добавив настроенный SSR-обмен в нужный индекс массива.

Соответствующий код для настройки выглядит следующим образом:

// ...
import urql, { cacheExchange, dedupExchange, fetchExchange, ssrExchange } from "@urql/vue";

// ...
  // Create SSR exchange
  const ssr = ssrExchange({
    isClient: process.client,
  });

  // Extract SSR payload once app is rendered on the server
  if (process.server) {
    nuxtApp.hook("app:rendered", () => {
      nuxtApp.payload?.data && (nuxtApp.payload.data.urql = ssr.extractData());
    });
  }

  // Restore SSR payload once app is created on the client
  if (process.client) {
    nuxtApp.hook("app:created", () => {
      nuxtApp.payload?.data && ssr.restoreData(nuxtApp.payload.data.urql);
    });
  }

  // Custom exchanges
  const exchanges = [dedupExchange, cacheExchange, ssr, fetchExchange];
// ...
Войти в полноэкранный режим Выйти из полноэкранного режима

Массив exchanges затем передается в опции urql для переопределения стандартных.

Я не уверен на 100% в приведенном выше коде обработки полезной нагрузки сервера/клиента, но он работает (возможно, есть какие-то помощники Nuxt для достижения этого более простым способом).

Бонус: Настройка обмена devtools

Может быть полезно отлаживать urql с помощью его официального devtools. Давайте добавим его в наши зависимости:

yarn add -D @urql/devtools
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем мы можем добавить его к нашим пользовательским обменам в plugins/urql.ts:

import { devtoolsExchange } from "@urql/devtools";

// ...
  // Devtools exchange
  if (nuxtApp._legacyContext?.isDev) {
    exchanges.unshift(devtoolsExchange);
  }
// ...
Войти в полноэкранный режим Выйти из полноэкранного режима

И вуаля, наше приложение теперь должно отображать отладочные события в панели devtools, при условии, что расширение правильно установлено в вашем браузере.

Использование сгенерированных операций

Поскольку наши сгенерированные операции могут быть автоматически импортированы (см. предыдущую статью), выполнить наш запрос { hello } очень просто:

<script setup lang="ts">
const { data } = await useHelloQuery();
</script>

<template>
  <div id="front-page" class="prose">
    <h1>{{ data?.hello || "Nuxt3 ❤️ GraphQL" }}</h1>
  </div>
</template>
Войти в полноэкранный режим Выйти из полноэкранного режима

Насколько это удивительно? Благодаря синтаксису <script setup> и автоимпортированию, одной строки TypeScript достаточно, чтобы выполнить нашу сгенерированную операцию запроса и получить доступ к результату с полной безопасностью типов!

Что насчет мутаций (и подписок)?

Использование сгенерированных операций мутации не сильно отличается от использования запросов, главное отличие в том, что мутация не выполняется автоматически. Вместо этого мы должны использовать метод executeMutation на объекте результата, возвращенном из нашей сгенерированной композиции, т.е. useSomeMutation.

Что касается подписок, то для их использования нам потребуется настроить какой-то вид транспорта. Этому будет посвящена следующая статья в этом увлекательном цикле!

За дополнительной информацией обращайтесь к документации @urql/vue по мутациям и подпискам.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *