Интегрируйте Firebase с приложением Next.js

1. Прежде чем начать

В этом практическом занятии вы узнаете, как интегрировать Firebase с веб-приложением на Next.js под названием Friendly Eats, которое представляет собой сайт для отзывов о ресторанах.

Веб-приложение Friendly Eats

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

  • Автоматическая сборка и развертывание: В этом практическом занятии используется Firebase App Hosting для автоматической сборки и развертывания вашего кода Next.js каждый раз, когда вы отправляете изменения в настроенную ветку.
  • Вход и выход: готовое веб-приложение позволяет входить и выходить из системы с помощью Google. Вход и сохранение данных пользователя полностью управляются через Firebase Authentication .
  • Изображения: Готовое веб-приложение позволяет авторизованным пользователям загружать изображения ресторанов. Изображения хранятся в Cloud Storage for Firebase . SDK Firebase JavaScript предоставляет общедоступный URL-адрес для загруженных изображений. Этот общедоступный URL-адрес затем сохраняется в соответствующем документе ресторана в Cloud Firestore .
  • Отзывы: Завершенное веб-приложение позволяет авторизованным пользователям оставлять отзывы о ресторанах, состоящие из звездного рейтинга и текстового сообщения. Информация об отзывах хранится в Cloud Firestore.
  • Фильтры: Готовое веб-приложение позволяет авторизованным пользователям фильтровать список ресторанов по категории, местоположению и цене. Также можно настроить используемый метод сортировки. Доступ к данным осуществляется из Cloud Firestore, а запросы Firestore применяются на основе используемых фильтров.

Предварительные требования

  • Аккаунт на GitHub
  • Знание Next.js и JavaScript.

Что вы узнаете

Что вам понадобится

  • Гит
  • Последняя стабильная версия Node.js
  • Любой браузер на ваш выбор, например, Google Chrome.
  • Среда разработки с редактором кода и терминалом.
  • Учетная запись Google для создания и управления вашим проектом Firebase.
  • Возможность перевести ваш проект Firebase на тарифный план Blaze.

2. Настройте среду разработки и репозиторий GitHub.

Данный практический урок предоставляет базовый код приложения и основан на использовании Firebase CLI.

Создайте репозиторий GitHub.

Исходный код для этого практического занятия можно найти по адресу https://github.com/firebase/friendlyeats-web . В репозитории содержатся примеры проектов для различных платформ. Однако в этом практическом занятии используется только директория nextjs-start . Обратите внимание на следующие директории:

* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.

Скопируйте папку nextjs-start в свой репозиторий:

  1. С помощью терминала создайте на компьютере новую папку и перейдите в неё:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Используйте npm-пакет giget , чтобы загрузить только папку nextjs-start :
    npx giget@latest "gh:firebase/friendlyeats-web/nextjs-start#master" . --install
    
  3. Отслеживайте изменения локально с помощью Git:
    git init
    
    git add .
    
    git commit -m "Codelab starting point"
    
    git branch -M main
    
  4. Создайте новый репозиторий GitHub: https://github.com/new . Назовите его как угодно.
  5. В зависимости от способа аутентификации в GitHub (HTTPS или SSH), скопируйте новый URL-адрес, который создаст для вас GitHub:
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git или
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. Отправьте локальные изменения в свой новый репозиторий GitHub, выполнив следующую команду. Замените URL-адрес вашего репозитория на URL-адрес заполнителя <REPOSITORY_URL> .
    git remote add origin <REPOSITORY_URL>
    
    git push -u origin main
    
  7. Теперь вы должны увидеть стартовый код в своем репозитории GitHub.

Установите или обновите Firebase CLI.

Выполните следующую команду, чтобы убедиться, что у вас установлен Firebase CLI и что его версия не ниже 14.1.0:

firebase --version

Если вы видите более старую версию или у вас не установлен Firebase CLI, выполните команду установки:

npm install -g firebase-tools@latest

Если вам не удаётся установить Firebase CLI из-за ошибок доступа, обратитесь к документации npm или воспользуйтесь другим вариантом установки .

Войдите в Firebase

  1. Для входа в Firebase CLI выполните следующую команду:
    firebase login
    
  2. В зависимости от того, хотите ли вы, чтобы Firebase собирал данные, введите Y или N
  3. В браузере выберите свой аккаунт Google, а затем нажмите «Разрешить» .

3. Настройте свой проект Firebase.

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

Создайте проект Firebase.

  1. Войдите в консоль Firebase, используя ту же учетную запись Google, которую вы использовали на предыдущем шаге.
  2. Нажмите кнопку, чтобы создать новый проект, а затем введите название проекта (например, FriendlyEats Codelab ).
  3. Нажмите «Продолжить» .
  4. Если появится запрос, ознакомьтесь с условиями использования Firebase и примите их, после чего нажмите «Продолжить» .
  5. (Необязательно) Включите помощь ИИ в консоли Firebase (в Firebase она называется "Gemini").
  6. Для этого практического занятия вам не понадобится Google Analytics, поэтому отключите эту опцию.
  7. Нажмите «Создать проект» , дождитесь завершения подготовки проекта, а затем нажмите «Продолжить» .

Обновите свой тарифный план Firebase.

Для использования Firebase App Hosting и Cloud Storage for Firebase ваш проект Firebase должен быть привязан к тарифному плану с оплатой по мере использования (Blaze) , то есть он должен быть связан с учетной записью Cloud Billing .

Чтобы перейти на тарифный план Blaze для вашего проекта, выполните следующие шаги:

  1. В консоли Firebase выберите вариант обновления вашего тарифного плана .
  2. Выберите тарифный план Blaze. Следуйте инструкциям на экране, чтобы связать учетную запись Cloud Billing с вашим проектом.
    Если в рамках этого обновления вам потребовалось создать учетную запись Cloud Billing, возможно, вам нужно будет вернуться к процессу обновления в консоли Firebase, чтобы завершить обновление.

Добавьте веб-приложение в свой проект Firebase.

  1. Перейдите в раздел «Обзор проекта» в вашем проекте Firebase, нажмите «Добавить приложение» , а затем — «Веб» .
  2. В текстовом поле «Псевдоним приложения» введите запоминающийся псевдоним приложения, например, My Next.js app .
  3. Оставьте флажок «Также настройте Firebase Hosting для этого приложения» снятым.
  4. Нажмите «Зарегистрировать приложение» > «Продолжить в консоли» .

Настройте службы Firebase в консоли Firebase.

Настройка аутентификации

  1. В левой панели консоли Firebase разверните раздел «Сборка» , а затем выберите «Аутентификация» .
  2. Нажмите « Начать» .
  3. В столбце «Поставщики авторизации» нажмите Google > Включить .
  4. В текстовом поле «Публичное имя проекта» введите запоминающееся имя, например, My Next.js app .
  5. В раскрывающемся списке «Адрес электронной почты службы поддержки для проекта» выберите свой адрес электронной почты.
  6. Нажмите « Сохранить ».

Настройка Cloud Firestore

  1. В левой панели консоли Firebase разверните раздел «Сборка» , а затем выберите «База данных Firestore» .
  2. Нажмите «Создать базу данных» .
  3. Выберите «Стандартная версия» и нажмите «Далее» .
  4. Не изменяйте идентификатор базы данных , оставьте его значение по (default) .
  5. Выберите местоположение для вашей базы данных, затем нажмите «Далее» .
    Для создания настоящего приложения вам следует выбрать местоположение, расположенное недалеко от ваших пользователей.
  6. Нажмите «Пуск» в тестовом режиме . Ознакомьтесь с отказом от ответственности в отношении правил безопасности.
    В дальнейшем в этом практическом занятии вы добавите правила безопасности для защиты ваших данных. Не распространяйте и не предоставляйте публичный доступ к приложению, не добавив правила безопасности для вашей базы данных.
  7. Нажмите «Создать» .

Настройка облачного хранилища для Firebase

  1. В левой панели консоли Firebase разверните раздел «Сборка» , а затем выберите «Хранилище» .
  2. Нажмите « Начать» .
  3. Выберите местоположение для вашего хранилища по умолчанию.
    Для регионов US-WEST1 , US-CENTRAL1 и US-EAST1 доступен тариф "Всегда бесплатно" от Google Cloud Storage. Для регионов во всех остальных регионах действуют тарифные планы и правила использования Google Cloud Storage .
  4. Нажмите «Пуск» в тестовом режиме . Ознакомьтесь с отказом от ответственности в отношении правил безопасности.
    В дальнейшем в этом практическом занятии вы добавите правила безопасности для защиты ваших данных. Не распространяйте и не предоставляйте публичный доступ к приложению без добавления правил безопасности для вашего хранилища.
  5. Нажмите «Создать» .

Внедрите правила безопасности.

В коде уже есть наборы правил безопасности для Firestore и для Cloud Storage for Firebase. После развертывания правил безопасности данные в вашей базе данных и вашем хранилище будут лучше защищены от неправомерного использования.

  1. В терминале настройте CLI для использования созданного ранее проекта Firebase:
    firebase use --add
    
    Когда появится запрос на ввод псевдонима, введите friendlyeats-codelab .
  2. Для развертывания этих правил безопасности (а также индексов , которые понадобятся позже) выполните следующую команду в терминале:
    firebase deploy --only firestore,storage
    
  3. Если вас спросят: "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?" , нажмите Enter и выберите «Да» .

4. Просмотрите исходный код.

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

Структура папок и файлов

В таблице ниже представлен обзор структуры папок и файлов приложения:

Папки и файлы

Описание

src/components

Компоненты React для фильтров, заголовков, подробной информации о ресторане и отзывов.

src/lib

Вспомогательные функции, которые не обязательно привязаны к React или Next.js.

src/lib/firebase

Специфический код Firebase и конфигурация Firebase

public

Статические элементы в веб-приложении, такие как иконки.

src/app

Маршрутизация с помощью маршрутизатора приложений Next.js

package.json и package-lock.json

Зависимости проекта с помощью npm

next.config.js

Конфигурация, специфичная для Next.js (действия сервера включены ).

jsconfig.json

конфигурация языковой службы JavaScript

Серверные и клиентские компоненты

Приложение представляет собой веб-приложение Next.js, использующее App Router . В приложении используется серверный рендеринг. Например, файл src/app/page.js — это серверный компонент, отвечающий за главную страницу. Файл src/components/RestaurantListings.jsx — это клиентский компонент, обозначенный директивой "use client" в начале файла.

Импорт операторов

Вы можете заметить операторы импорта, подобные следующим:

import RatingPicker from "@/src/components/RatingPicker.jsx";

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

API, специфичные для Firebase

Весь код API Firebase находится в директории src/lib/firebase . Затем отдельные компоненты React импортируют обернутые функции из директории src/lib/firebase , а не напрямую импортируют функции Firebase.

Имитационные данные

Имитация данных о ресторанах и отзывах содержится в файле src/lib/randomData.js . Данные из этого файла собираются в коде в файле src/lib/fakeRestaurants.js .

5. Создайте бэкэнд для размещения приложений.

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

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

Создайте бэкэнд.

  1. Перейдите на страницу «Размещение приложений» в консоли Firebase.
  2. Нажмите « Начать» , чтобы запустить процесс создания административной панели.
  3. Выберите регион. Для настоящего приложения вы бы выбрали регион, ближайший к вашим пользователям.
  4. Следуйте инструкциям на шаге «Импорт репозитория GitHub» , чтобы настроить аутентификацию GitHub.
  5. В разделе «Репозиторий» выберите «Предоставить доступ к новому репозиторию в GitHub» и следуйте инструкциям, чтобы включить доступ к созданному ранее репозиторию GitHub.
  6. Нажмите «Обновить список» , чтобы обновить список, затем выберите свой репозиторий и нажмите «Далее» .
  7. Настройте параметры развертывания:
    1. Установите рабочую ветку на main .
    2. Оставьте корневой каталог в качестве / .
    3. Включите автоматическое развертывание.
  8. Назовите свой бэкэнд friendlyeats-codelab и нажмите «Далее» .
  9. В разделе «Связать веб-приложение Firebase» выберите «Выбрать существующее веб-приложение Firebase» и выберите добавленное вами приложение из списка.
  10. Нажмите «Готово» и «Развернуть» . Вы будете перенаправлены на новую страницу, где сможете увидеть статус вашей новой серверной части для размещения приложений!
  11. Нажмите «Просмотреть» , чтобы увидеть дополнительную информацию о развертывании вашего приложения, включая статус развертывания, журналы и сведения об использовании.
  12. После завершения развертывания нажмите, чтобы открыть URL-адрес вашего сайта в разделе «Домены» . Для начала работы может потребоваться несколько минут из-за распространения DNS-записей.
  13. Ой-ой! При загрузке страницы вы увидите сообщение об ошибке: «Ошибка приложения: произошла ошибка на стороне сервера (подробнее см. в журналах сервера)».
  14. В консоли Firebase проверьте вкладку « Журналы» в бэкэнде вашего приложения. Вы увидите запись «Ошибка: не реализовано». Мы исправим это на следующем шаге, когда добавим аутентификацию.

Вы успешно развернули исходное веб-приложение! Каждый раз, когда вы отправляете новый коммит в main ветку вашего репозитория GitHub, в консоли Firebase начинается новая сборка и развертывание, и ваш сайт автоматически обновляется после завершения развертывания.

6. Добавьте аутентификацию в веб-приложение.

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

Добавить авторизованный домен

Аутентификация Firebase принимает запросы на вход только с разрешенных вами доменов. Здесь мы добавим домен вашего бэкенда App Hosting в список разрешенных доменов в вашем проекте.

  1. Откройте страницу «Хостинг приложений» и нажмите «Просмотреть» под развернутым сайтом, чтобы перейти на страницу «Обзор» . Скопируйте доменное имя вашей серверной части хостинга приложений.
  2. Перейдите на вкладку «Настройки аутентификации» и выберите проект, к которому хотите добавить авторизованный домен. Затем найдите раздел «Авторизованные домены» и нажмите на него.
  3. Нажмите кнопку «Добавить домен» .
  4. Введите домен вашей административной панели хостинга приложений.
  5. Нажмите «Добавить» .

Реализуйте функции входа и выхода из системы.

В файле src/lib/firebase/auth.js замените функции onAuthStateChanged , onIdTokenChanged , signInWithGoogle и signOut следующим кодом:

export function onAuthStateChanged(cb) {
  return _onAuthStateChanged(auth, cb);
}

export function onIdTokenChanged(cb) {
  return _onIdTokenChanged(auth, cb);
}

export async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();

  try {
    await signInWithPopup(auth, provider);
  } catch (error) {
    console.error("Error signing in with Google", error);
  }
}

export async function signOut() {
  try {
    return auth.signOut();
  } catch (error) {
    console.error("Error signing out with Google", error);
  }
}

В этом коде используются следующие API Firebase:

API Firebase

Описание

auth.onAuthStateChanged

Добавляет наблюдателя для отслеживания изменений состояния авторизации пользователя.

auth.onIdTokenChanged

Добавляет наблюдателя для отслеживания изменений токена идентификации пользователя.

GoogleAuthProvider

Создает экземпляр поставщика аутентификации Google.

signInWithPopup

Запускает процесс аутентификации на основе диалога.

auth.signOut

Выводит пользователя из системы.

В файле src/components/Header.jsx код уже вызывает функции signInWithGoogle и signOut .

Передайте состояние аутентификации на сервер.

Для передачи состояния аутентификации на сервер мы будем использовать cookie-файлы. При каждом изменении состояния аутентификации на стороне клиента мы будем обновлять cookie-файл __session .

В src/components/Header.jsx замените функцию useUserSession следующим кодом:

function useUserSession(initialUser) {
  useEffect(() => {
    return onIdTokenChanged(async (user) => {
      if (user) {
        const idToken = await user.getIdToken();
        await setCookie("__session", idToken);
      } else {
        await deleteCookie("__session");
      }
      if (initialUser?.uid === user?.uid) {
        return;
      }
      window.location.reload();
    });
  }, [initialUser]);

  return initialUser;
}

Прочитать состояние аутентификации на сервере

Мы будем использовать FirebaseServerApp для зеркалирования состояния аутентификации клиента на сервере.

Откройте src/lib/firebase/serverApp.js и замените функцию getAuthenticatedAppForUser :

export async function getAuthenticatedAppForUser() {
  const authIdToken = (await cookies()).get("__session")?.value;

  // Firebase Server App is a new feature in the JS SDK that allows you to
  // instantiate the SDK with credentials retrieved from the client & has
  // other affordances for use in server environments.
  const firebaseServerApp = initializeServerApp(
    // https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
    initializeApp(),
    {
      authIdToken,
    }
  );

  const auth = getAuth(firebaseServerApp);
  await auth.authStateReady();

  return { firebaseServerApp, currentUser: auth.currentUser };
}

Проверить изменения

Основной макет в файле src/app/layout.js отображает заголовок и передает пользователя, если он доступен, в качестве свойства.

<Header initialUser={currentUser?.toJSON()} />

Это означает, что компонент <Header> отображает данные пользователя, если они доступны, во время работы сервера. Если после первоначальной загрузки страницы в течение жизненного цикла страницы происходят какие-либо обновления аутентификации, их обрабатывает обработчик onAuthStateChanged .

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

  1. Создайте коммит с сообщением "Добавить аутентификацию" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Add authentication"
    
    git push
    
  2. Откройте страницу «Размещение приложений» и, когда развертывание будет завершено, щелкните URL-адрес сайта, чтобы открыть его.
  3. Проверка аутентификации:
    1. Войдите в систему, используя свою учетную запись Google, и убедитесь, что ваше отображаемое имя отображается в заголовке после входа.
    2. Выйдите из системы и войдите снова. Вы можете повторить этот шаг с разными пользователями.
    3. Необязательно: Щелкните правой кнопкой мыши веб-приложение, выберите «Просмотреть исходный код страницы» и найдите отображаемое имя. Оно отображается в исходном HTML-коде, возвращаемом сервером.

7. Просмотреть информацию о ресторане

Веб-приложение включает в себя тестовые данные для ресторанов и отзывов.

Добавить один или несколько ресторанов

Чтобы добавить фиктивные данные о ресторане в локальную базу данных Cloud Firestore, выполните следующие действия:

  1. Если вы еще этого не сделали, войдите в веб-приложение. Затем выберите 2cf67d488d8e6332.png > Добавьте примеры ресторанов . Обратите внимание, что в веб-приложении Friendly Eats рестораны не отображаются, потому что мы еще не настроили код для получения данных. Мы исправим это на следующем шаге.
  2. В консоли Firebase на странице базы данных Firestore выберите раздел «Рестораны» . Вы увидите документы верхнего уровня в коллекции «Рестораны», каждый из которых представляет собой отдельный ресторан.
  3. Щелкните по нескольким документам, чтобы изучить свойства документа, относящегося к ресторану.

Отобразить список ресторанов

В вашей базе данных Cloud Firestore теперь есть рестораны, которые может отображать веб-приложение Next.js.

Для определения кода получения данных выполните следующие шаги:

  1. В файле src/app/page.js найдите компонент сервера <Home /> и изучите вызов функции getRestaurants , которая получает список ресторанов во время выполнения на сервере. Реализуйте функцию getRestaurants на следующих шагах.
  2. В файле src/lib/firebase/firestore.js замените функции applyQueryFilters и getRestaurants следующим кодом:
function applyQueryFilters(q, { category, city, price, sort }) {
  if (category) {
    q = query(q, where("category", "==", category));
  }
  if (city) {
    q = query(q, where("city", "==", city));
  }
  if (price) {
    q = query(q, where("price", "==", price.length));
  }
  if (sort === "Rating" || !sort) {
    q = query(q, orderBy("avgRating", "desc"));
  } else if (sort === "Review") {
    q = query(q, orderBy("numRatings", "desc"));
  }
  return q;
}

export async function getRestaurants(db = db, filters = {}) {
  let q = query(collection(db, "restaurants"));

  q = applyQueryFilters(q, filters);
  const results = await getDocs(q);
  return results.docs.map((doc) => {
    return {
      id: doc.id,
      ...doc.data(),
      // Only plain objects can be passed to Client Components from Server Components
      timestamp: doc.data().timestamp.toDate(),
    };
  });
}
  1. Создайте коммит с сообщением "Прочитать список ресторанов из Firestore" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Read the list of restaurants from Firestore"
    
    git push
    
  2. Откройте страницу «Размещение приложений» в консоли Firebase и дождитесь завершения развертывания.
  3. В веб-приложении обновите страницу. Изображения ресторанов отобразятся на странице в виде плиток.

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

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

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

  1. В веб-приложении откройте инструменты разработчика и отключите JavaScript .

Отключение JavaScript в инструментах разработчика

  1. Обновите веб-приложение. Список ресторанов по-прежнему загружается. Информация о ресторане возвращается в ответе сервера. Если JavaScript включен, информация о ресторане заполняется с помощью клиентского JavaScript-кода.
  2. В инструментах разработчика повторно включите JavaScript .

Отслеживайте обновления ресторанов с помощью слушателей снимков Cloud Firestore.

В предыдущем разделе вы видели, как загружается начальный набор ресторанов из файла src/app/page.js . Файл src/app/page.js является серверным компонентом и отображается на сервере, включая код получения данных из Firebase.

Файл src/components/RestaurantListings.jsx является клиентским компонентом и может быть настроен для обработки разметки, генерируемой на сервере.

Чтобы настроить файл src/components/RestaurantListings.jsx для обработки разметки, генерируемой сервером, выполните следующие действия:

  1. В файле src/components/RestaurantListings.jsx обратите внимание на следующий код, который уже написан за вас:
useEffect(() => {
    return getRestaurantsSnapshot((data) => {
      setRestaurants(data);
    }, filters);
  }, [filters]);

Этот код вызывает функцию getRestaurantsSnapshot() , которая аналогична функции getRestaurants() , реализованной вами на предыдущем шаге. Однако эта функция создания снимка предоставляет механизм обратного вызова, благодаря которому функция обратного вызова вызывается каждый раз, когда в коллекцию ресторанов вносятся изменения.

  1. В файле src/lib/firebase/firestore.js замените функцию getRestaurantsSnapshot() следующим кодом:
export function getRestaurantsSnapshot(cb, filters = {}) {
  if (typeof cb !== "function") {
    console.log("Error: The callback parameter is not a function");
    return;
  }

  let q = query(collection(db, "restaurants"));
  q = applyQueryFilters(q, filters);

  return onSnapshot(q, (querySnapshot) => {
    const results = querySnapshot.docs.map((doc) => {
      return {
        id: doc.id,
        ...doc.data(),
        // Only plain objects can be passed to Client Components from Server Components
        timestamp: doc.data().timestamp.toDate(),
      };
    });

    cb(results);
  });
}
  1. В файле src/lib/firebase/firestore.js замените функцию getRestaurantSnapshotById() следующим кодом:
export function getRestaurantSnapshotById(restaurantId, cb) {
  if (!restaurantId) {
    console.log("Error: Invalid ID received: ", restaurantId);
    return;
  }

  if (typeof cb !== "function") {
    console.log("Error: The callback parameter is not a function");
    return;
  }

  const docRef = doc(db, "restaurants", restaurantId);
  return onSnapshot(docRef, (docSnap) => {
    cb({
      ...docSnap.data(),
      timestamp: docSnap.data().timestamp.toDate(),
    });
  });
}

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

  1. Создайте коммит с сообщением "Следите за обновлениями ресторана в режиме реального времени" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Listen for realtime restaurant updates"
    
    git push
    
  2. Откройте страницу «Размещение приложений» в консоли Firebase и дождитесь завершения развертывания.
  3. В веб-приложении выберите 27ca5d1e8ed8adfe.png > Добавьте примеры ресторанов . Если функция создания снимков реализована правильно, рестораны отображаются в режиме реального времени без перезагрузки страницы.

8. Сохраняйте отзывы пользователей из веб-приложения.

  1. В файле src/lib/firebase/firestore.js замените функцию updateWithRating() следующим кодом:
const updateWithRating = async (
  transaction,
  docRef,
  newRatingDocument,
  review
) => {
  const restaurant = await transaction.get(docRef);
  const data = restaurant.data();
  const newNumRatings = data?.numRatings ? data.numRatings + 1 : 1;
  const newSumRating = (data?.sumRating || 0) + Number(review.rating);
  const newAverage = newSumRating / newNumRatings;

  transaction.update(docRef, {
    numRatings: newNumRatings,
    sumRating: newSumRating,
    avgRating: newAverage,
  });

  transaction.set(newRatingDocument, {
    ...review,
    timestamp: Timestamp.fromDate(new Date()),
  });
};

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

  1. Замените функцию addReviewToRestaurant() следующим кодом:
export async function addReviewToRestaurant(db, restaurantId, review) {
  if (!restaurantId) {
    throw new Error("No restaurant ID has been provided.");
  }

  if (!review) {
    throw new Error("A valid review has not been provided.");
  }

  try {
    const docRef = doc(collection(db, "restaurants"), restaurantId);
    const newRatingDocument = doc(
      collection(db, `restaurants/${restaurantId}/ratings`),
    );

    // corrected line
    await runTransaction(db, (transaction) =>
      updateWithRating(transaction, docRef, newRatingDocument, review),
    );
  } catch (error) {
    console.error(
      "There was an error adding the rating to the restaurant",
      error,
    );
    throw error;
  }
}

Реализуйте серверное действие Next.js.

Действие сервера Next.js предоставляет удобный API для доступа к данным формы, например, data.get("text") для получения текстового значения из данных, отправленных через форму.

Чтобы использовать действие Next.js Server Action для обработки отправки формы отзыва, выполните следующие шаги:

  1. В файле src/components/ReviewDialog.jsx найдите атрибут action в элементе <form> .
<form
  action={handleReviewFormSubmission}
  onSubmit={() => {
    handleClose();
  }}
>

Значение атрибута action указывает на функцию, которую вы реализуете на следующем шаге.

  1. В файле src/app/actions.js замените функцию handleReviewFormSubmission() следующим кодом:
export async function handleReviewFormSubmission(data) {
  const { firebaseServerApp } = await getAuthenticatedAppForUser();
  const db = getFirestore(firebaseServerApp);

  await addReviewToRestaurant(db, data.get("restaurantId"), {
    text: data.get("text"),
    rating: data.get("rating"),

    // This came from a hidden form field.
    userId: data.get("userId"),
  });
}

Добавить отзывы о ресторане

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

Чтобы добавить отзыв и убедиться, что он добавлен в Cloud Firestore, выполните следующие действия:

  1. Создайте коммит с сообщением "Разрешить пользователям оставлять отзывы о ресторанах" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Allow users to submit restaurant reviews"
    
    git push
    
  2. Откройте страницу «Размещение приложений» в консоли Firebase и дождитесь завершения развертывания.
  3. Обновите веб-приложение и выберите ресторан на главной странице.
  4. На странице ресторана нажмите 3e19beef78bb0d0e.png .
  5. Выберите оценку в виде звезд.
  6. Напишите отзыв.
  7. Нажмите «Отправить» . Ваш отзыв появится вверху списка отзывов.
  8. В Cloud Firestore найдите в панели «Добавить документ» документ ресторана, который вы оценили, и выберите его.
  9. В панели «Начало коллекции» выберите «Рейтинги» .
  10. В панели «Добавить документ» найдите документ для проверки, чтобы убедиться, что он был вставлен должным образом.

9. Сохраняйте файлы, загруженные пользователем из веб-приложения.

В этом разделе вы добавляете функциональность, позволяющую заменять изображение, связанное с рестораном, при авторизации. Вы загружаете изображение в Firebase Storage и обновляете URL-адрес изображения в документе Cloud Firestore, представляющем ресторан.

Чтобы сохранить файлы, загруженные пользователем из веб-приложения, выполните следующие действия:

  1. В файле src/components/Restaurant.jsx обратите внимание на код, который выполняется, когда пользователь загружает файл:
async function handleRestaurantImage(target) {
  const image = target.files ? target.files[0] : null;
  if (!image) {
    return;
  }

  const imageURL = await updateRestaurantImage(id, image);
  setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}

Вносить изменения в эту функцию не требуется, но на следующих шагах вам нужно будет реализовать поведение функции updateRestaurantImage() .

  1. В файле src/lib/firebase/storage.js замените функции updateRestaurantImage() и uploadImage() следующим кодом:
export async function updateRestaurantImage(restaurantId, image) {
  try {
    if (!restaurantId) {
      throw new Error("No restaurant ID has been provided.");
    }

    if (!image || !image.name) {
      throw new Error("A valid image has not been provided.");
    }

    const publicImageUrl = await uploadImage(restaurantId, image);
    await updateRestaurantImageReference(restaurantId, publicImageUrl);

    return publicImageUrl;
  } catch (error) {
    console.error("Error processing request:", error);
  }
}

async function uploadImage(restaurantId, image) {
  const filePath = `images/${restaurantId}/${image.name}`;
  const newImageRef = ref(storage, filePath);
  await uploadBytesResumable(newImageRef, image);

  return await getDownloadURL(newImageRef);
}

Функция updateRestaurantImageReference() уже реализована. Эта функция обновляет существующий документ ресторана в Cloud Firestore, добавляя обновленный URL изображения.

Проверьте работоспособность функции загрузки изображений.

Чтобы убедиться, что изображение загружается должным образом, выполните следующие действия:

  1. Создайте коммит с сообщением "Разрешить пользователям изменять фотографии каждого ресторана" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Allow users to change each restaurants' photo"
    
    git push
    
  2. Откройте страницу «Размещение приложений» в консоли Firebase и дождитесь завершения развертывания.
  3. В веб-приложении убедитесь, что вы вошли в систему, и выберите ресторан.
  4. Нажмите 7067eb41fea41ff0.png и загрузите изображение из вашей файловой системы. Ваше изображение покинет локальную среду и будет загружено в облачное хранилище. Изображение появится сразу после загрузки.
  5. Перейдите в раздел «Облачное хранилище для Firebase».
  6. Перейдите в папку, соответствующую ресторану. Загруженное вами изображение находится в этой папке.

6cf3f9e2303c931c.png

10. Обобщите отзывы о ресторанах с помощью генеративного искусственного интеллекта.

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

Сохраните ключ API Gemini в Cloud Secret Manager.

  1. Для использования API Gemini вам потребуется ключ API. Зайдите в Google AI Studio и нажмите «Создать ключ API» .
  2. Назовите ключ как угодно. Если вашего проекта нет в списке « Выберите импортированный проект» , нажмите «Импорт проекта» , отметьте свой проект в списке, а затем нажмите «Импорт» . Наконец, выберите его в списке «Выберите импортированный проект» и нажмите «Создать ключ» .
  3. Интеграция App Hosting с Cloud Secret Manager позволяет безопасно хранить конфиденциальные данные, такие как ключи API:
    1. В терминале выполните команду для создания нового секрета:
    firebase apphosting:secrets:set GEMINI_API_KEY
    
    1. Когда система запросит секретное значение, скопируйте и вставьте свой API-ключ Gemini из Google AI Studio.
    2. Когда вас спросят, предназначен ли новый секрет для использования в производственной среде или для локального тестирования, выберите «Производственная среда».
    3. Когда вас спросят, хотите ли вы предоставить доступ к секретному ключу учетной записи службы вашего бэкэнда, выберите «Да».
    4. Когда появится запрос о добавлении нового секретного ключа в файл apphosting.yaml , введите Y для подтверждения.

Ваш API-ключ Gemini теперь надежно хранится в Cloud Secret Manager и доступен для бэкэнда вашего хостинга приложений.

Внедрить компонент сводки обзора.

  1. В src/components/Reviews/ReviewSummary.jsx замените функцию GeminiSummary следующим кодом:
    export async function GeminiSummary({ restaurantId }) {
      const { firebaseServerApp } = await getAuthenticatedAppForUser();
      const reviews = await getReviewsByRestaurantId(
        getFirestore(firebaseServerApp),
        restaurantId
      );
    
      const reviewSeparator = "@";
      const prompt = `
        Based on the following restaurant reviews, 
        where each review is separated by a '${reviewSeparator}' character, 
        create a one-sentence summary of what people think of the restaurant. 
    
        Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)}
      `;
    
      try {
        if (!process.env.GEMINI_API_KEY) {
          // Make sure GEMINI_API_KEY environment variable is set:
          // https://genkit.dev/docs/get-started/
          throw new Error(
            'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"'
          );
        }
    
        // Configure a Genkit instance.
        const ai = genkit({
          plugins: [googleAI()],
          model: gemini20Flash, // set default model
        });
        const { text } = await ai.generate(prompt);
    
        return (
          <div className="restaurant__review_summary">
            <p>{text}</p>
            <p> Summarized with Gemini</p>
          </div>
        );
      } catch (e) {
        console.error(e);
        return <p>Error summarizing reviews.</p>;
      }
    }
    
  2. Создайте коммит с сообщением "Используйте ИИ для обобщения отзывов" и отправьте его в свой репозиторий GitHub.
    git add .
    
    git commit -m "Use AI to summarize reviews"
    
    git push
    
  3. Откройте страницу «Размещение приложений» в консоли Firebase и дождитесь завершения развертывания.
  4. Откройте страницу ресторана. Вверху вы увидите краткое описание всех отзывов на странице, состоящее из одного предложения.
  5. Добавьте новый отзыв и обновите страницу. Вы должны увидеть изменения в сводке.

11. Снимите свою учетную запись хостинга приложений с публикации.

После завершения этого практического занятия, если вы не собираетесь продолжать использовать приложение, вы можете удалить его из публикации, чтобы никто не получил доступ к вашим ресурсам Firestore, Storage и Gemini. Вы можете повторно опубликовать его в любое время.

Чтобы снять сайт с хостинга приложений с публикации:

  1. Откройте раздел «Хостинг приложений» в консоли Firebase .
  2. Найдите бэкэнд вашего приложения и нажмите «Просмотреть» .
  3. В разделе «Информация в административной панели» рядом с пунктом «Домены» нажмите «Управление» . Откроется страница «Домены» .
  4. Рядом с вашим доменом нажмите значок «Дополнительно» (три вертикальные точки), выберите «Отключить домен» , а затем нажмите «Отключить» для подтверждения.

12. Заключение

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

  • Firebase App Hosting позволяет автоматически собирать и развертывать ваш код Next.js каждый раз, когда вы отправляете изменения в настроенную ветку.
  • Аутентификация Firebase обеспечивает возможность входа и выхода из системы.
  • Cloud Firestore для хранения данных о ресторанах и отзывов о них.
  • Облачное хранилище Firebase для изображений ресторанов.

Узнать больше