Начало работы с клиентом Apollo в Next.js

Published on
Authors

Выяснение того, как настроить Apollo Client с помощью Next.js, может немного запутать. Эта путаница возникает из-за того, что существует три способа получения и визуализации данных: статический, на стороне сервера и на стороне клиента. У каждой стратегии есть свои преимущества и недостатки, но я рад вам сообщить, что Apollo Client совместим со всеми тремя!

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

Если вы не знакомы с тем, как страницы отображаются в Next.js, ознакомьтесь с этими руководствами по предварительному рендерингу и двум поддерживаемым Next.js типам предварительного рендеринга.
-

Быстрое сравнение вариантов получения данных в Next.js

Статический рендеринг

При статическом рендеринге ваши страницы создаются во время сборки, а затем используются как статические файлы HTML. Этот метод обеспечивает быстрое время ответа, поскольку не нужно отображать страницы для каждого запроса. SEO также работает должным образом для статически отрендеренных страниц, поэтому контент легко доступен для поисковых роботов. Однако статически отрендеренный контент можно обновить только путем деплоя новой версии вашего приложения. Это означает, что по сравнению с другими методами ваш контент с большей вероятностью устареет между деплоями.

Рендеринг на стороне сервера

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

Рендеринг на стороне клиента

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

Создание нового приложения Next.js

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

Чтобы создать новое приложение Next.js, мы будем использовать create-next-app:

npx create-next-app next-with-apollo

Вы можете изменить next-with-apollo на любое имя проекта или выбрать имя того проекта, который хотите создавать.

Когда шаблон приложения создан и установлены зависимости, теперь мы можем запустить приложение:

cd next-with-apollo
npm run dev

При переходе по адресу http://localhost:3000 в вашем браузере откроется работающее приложение Next.js!

Настройка клиента Apollo

Теперь, когда приложение создано, пора добавить Apollo Client и подключить его к API. В этом примере мы будем использовать API стран от Trevor Blades для отображения кодов стран и флагов для каждой страны.

Полный код можно найти здесь.

npm install @apollo/client graphql

Теперь, когда наши зависимости установлены, пришло время в нашем приложении настроить клиент. Создайте новый файл в корне проекта с именем apollo-client.js и добавьте в него следующий код:

// ./apollo-client.js

import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
    uri: "https://countries.trevorblades.com",
    cache: new InMemoryCache(),
});

export default client;

Теперь у нас есть клиент GraphQL, который мы можем использовать в приложении, и мы готовы запросить некоторые данные. Однако, прежде чем мы сможем запросить что-либо, нам сначала нужно узнать, какие данные доступны нам из API стран.

Изучение API

Мы будем использовать Apollo Explorer, мощную IDE GraphQL, для работы с API стран. Проводник подключается к графу и предоставляет рабочую область для создания, выполнения и сохранения запросов.

Если у вас еще нет учетной записи, вы можете создать ее, используя свою учетную запись GitHub. Когда вы войдете в систему, вам нужно будет нажать кнопку «New Graph» в правом верхнем углу.

Затем вам будет предложено дать новому графу название и выбрать тип графа. Вы также можете изменить конечную точку на

https://countries.trevorblades.com.

Как только граф будет создан, Apollo Studio переместит вас в Apollo Explorer.

Замените пример запроса в окне «Operations» следующим запросом.

query ExampleQuery {
  countries {
    code
    name
  }
}

И если мы запустим пример, щелкнув синюю кнопку плей, мы должны получить результат в формате JSON, подобный этому:

"data": {
    "countries": [
      {
        "code": "AD",
        "name": "Andorra"
      },
      {
        "code": "AE",
        "name": "United Arab Emirates"
      },
      {
        "code": "AF",
        "name": "Afghanistan"
      },
      {
        "code": "AG",
        "name": "Antigua and Barbuda"
      },
      ...
   ]
}

Теперь мы готовы вернуться в приложение и начать получение данных!

Использование Apollo Client для статически отображаемых данных страницы

Прежде чем мы сможем получать данные из API, мы должны настроить нашу страницу. Мы делаем это для статически сгенерированных страниц, используя метод getStaticProps. Next.js будет использовать эту функцию в процессе сборки для получения любых данных, которые необходимо передать компоненту страницы в качестве свойств.

Поскольку мы используем методы, предоставляемые Next.js для получения данных, мы не собираемся вызывать наш API из самого React. Поэтому мы будем использовать Apollo Client напрямую, а не использовать хуки.

Внутри pages/index.js импортировать клиента, который мы создали в apollo-client.js иgql из @apollo/client.

// pages/index.js

import { gql } from "@apollo/client";
import client from "../apollo-client";

Затем добавьте следующий код под компонентом Next.js (React):

// pages/index.js

export async function getStaticProps() {
    const { data } = await client.query({
      query: gql`
        query Countries {
          countries {
            code
            name
            emoji
          }
        }
      `,
    });

    return {
      props: {
        countries: data.countries.slice(0, 4),
      },
   };
}

Будет получен код, имя и эмодзи(флаг) для каждой страны, а затем мы передадим первые четыре результата компоненту Next.js(React) через пропс countries. Теперь, когда у нас есть данные, пора обновить компонент, чтобы отобразить данные страницы.

Чтобы использовать наши данные по странам, нам нужно обновить компонент Next.js(React) с помощью свойства countries.

// pages/index.js

export default function Home({ countries }) {
...

Имея страны, доступные в компоненте, мы можем заменить грид страницы следующим кодом:

// pages/index.js

<div className={styles.grid}>
  {countries.map((country) => (
    <div key={country.code} className={styles.card}>
      <h3>{country.name}</h3>
      <p>
        {country.code} - {country.emoji}
      </p>
    </div>
  ))}
</div>

Страница теперь включает четыре страны, которые мы получили из метода getStaticProps! Довольно круто!

Важно отметить, что Next.js будет получать данные во время сборки и, следовательно, будут устаревшими.
-

Использование Apollo Client для обработки данных страницы на стороне сервера

Получение данных для страниц, созданных на стороне сервера, выполняется с помощью метода getServerSideProps, предоставляемого Next.js. Эта функция будет использоваться во время каждого запроса страницы для получения любых данных, переданных в компонент страницы в качестве свойств.

Получение данных для страниц, отображаемых на стороне сервера, не сильно отличается от того, что мы делали для статически сгенерированных страниц. Фактически, он настолько похож, что мы можем использовать почти всю только что проделанную работу.

Дубликат ./pages/index.js и переименуйте его в server-side.js. Затем измените имя функции, которая извлекает данные c getStaticProps на getServerSideProps.

// pages/server-side.js

export async function getServerSideProps() {
  const { data } = await client.query({
    query: gql`
      query Countries {
        countries {
          code
          name
          emoji
        }
      }
    `,
  });

  return {
    props: {
      countries: data.countries.slice(0, 4),
    },
  };
}

Все остальное остается прежним!

Основное отличие здесь в том, что Next.js будет получать коды стран в этом примере при каждом запросе страницы, а не только во время сборки.

Использование Apollo Client для данных на стороне клиента

Рендеринг на стороне клиента — это то, что мы обычно делаем в приложениях React. Браузер запрашивает приложение, страница загружается, затем React запрашивает данные и представляет их пользователю.

Настройка Apollo Client для использования на стороне клиента требует нескольких дополнительных шагов. Поскольку мы будем в React и захотим использовать хуки, нам понадобится ApolloProvider. Откройте ./pages/_app.js и импортируйте ApolloProvider из @apollo/client, а также клиента, который мы создали ранее.

// pages/_app.js

import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client";

Теперь замените компонент в _app.js следующим:

// pages/_app.js

function MyApp({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

Когда провайдер оборачивает наш корневой компонент, мы можем использовать хуки в любом месте нашего приложения, но мы еще не закончили. Если мы начнем использовать хуки в нашем приложении сейчас, мы закончим тем, что будем делать запросы к нашему API во время рендеринга страницы (либо во время сборки, либо во время обслуживания, в зависимости от того, как настроена страница). Это не идеально, потому что страница будет сгенерирована до того, как запросы смогут вернуться, а Next.js может передать данные компоненту.

Создайте новый каталог с именем componentsв корне проекта, а затем файл внутри этой папки с именем ClientOnly.js.

Скопируйте следующий код в новый файл:

// components/ClientOnly.js

import { useEffect, useState } from "react";

export default function ClientOnly({ children, ...delegated }) {
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => {
    setHasMounted(true);
  }, []);

  if (!hasMounted) {
    return null;
  }

  return <div {...delegated}>{children}</div>;
}

Чтобы убедиться, что мы запрашиваем данные только из браузера, мы должны убедиться, что компоненты, использующие хуки, отображаются только на клиенте. Мы можем добиться этого, создав компонент, который отображает своих дочерних элементов только в браузере, а не на сервере.

Чтобы узнать больше об этом шаблоне, ознакомьтесь со статьей Джоша Комо.
-

Затем создайте еще один файл в каталоге components назовите Countries.js и добавьте следующий код:


// components/Countries.js

import { useQuery, gql } from "@apollo/client";
import styles from "../styles/Home.module.css";

const QUERY = gql`
  query Countries {
    countries {
      code
      name
      emoji
    }
  }
`;

export default function Countries() {
  const { data, loading, error } = useQuery(QUERY);

  if (loading) {
    return <h2>Loading...</h2>;
  }

  if (error) {
    console.error(error);
    return null;
  }

  const countries = data.countries.slice(0, 4);

  return (
    <div className={styles.grid}>
      {countries.map((country) => (
        <div key={country.code} className={styles.card}>
          <h3>{country.name}</h3>
          <p>
            {country.code} - {country.emoji}
          </p>
        </div>
      ))}
    </div>
  );
}

Этот компонент отвечает за выборку стран и будет обернут в ClientOnly компонент, который мы создали ранее.

Наконец, создайте новый файл в pages каталоге и назовите client-side.js и добавьте следующий код:


// pages/client-side.js

import Head from "next/head";
import styles from "../styles/Home.module.css";
import ClientOnly from "../components/ClientOnly";
import Countries from "../components/Countries";

export default function ClientSide() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <ClientOnly>
          <Countries />
        </ClientOnly>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{" "}
          <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
        </a>
      </footer>
    </div>
  );
}

Эта страница очень похожа на две другие страницы, за исключением того, что на ней нет getStaticProps или getServerSideProps методы, потому что мы получим данные после отображения страницы в браузере.

И это все!👏

Теперь у нас есть получение данных на стороне клиента!

Вывод

Next.js предоставляет методы получения данных как для статически отрендеринных, так и для рендеринга на стороне сервера страниц через getStaticProps и getServerSideProps методы. Мы использовали Apollo Client напрямую для запроса API стран при работе в режиме рендеринга на стороне клиента.
На клиенте мы обернули корневой компонент в ApolloProvider.

Мы использовали настраиваемый компонент, чтобы гарантировать, что мы запрашиваем только страны у клиента, чтобы избежать отправки повторяющихся запросов во время рендеринга страницы.

В одном из следующих постов рассмотрим, как можно использовать эти шаблоны данных для более сложных случаев использования.
-