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

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

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

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

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

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

Предпосылки

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

Чему вы научитесь

  • Как использовать Firebase с Next.js App Router и рендерингом на стороне сервера.
  • Как сохранить изображения в облачном хранилище для Firebase.
  • Как читать и записывать данные в базу данных Cloud Firestore.
  • Как использовать вход через Google с Firebase JavaScript SDK.

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

  • Гит
  • Последняя стабильная версия 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. Скопируйте новый URL-адрес, который создаст GitHub. Он будет выглядеть примерно так:
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git или
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. Перенесите локальные изменения в новый репозиторий GitHub, выполнив следующую команду. Вместо заполнителя <REPOSITORY_URL> укажите фактический 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 (так называемая «Gemini в Firebase»).
  6. Для этой лабораторной работы вам не понадобится Google Analytics, поэтому отключите опцию Google Analytics.
  7. Нажмите «Создать проект» , дождитесь завершения подготовки проекта, а затем нажмите «Продолжить» .

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

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

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

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

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

  1. Перейдите к обзору проекта в вашем проекте Firebase и нажмите e41f2efdd9539c31.png Веб .

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

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

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

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

Настройка Cloud Firestore

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

Настройка облачного хранилища для 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. Создайте серверную часть хостинга приложений

В этом разделе вы настроите бэкэнд App Hosting для отслеживания ветки в вашем репозитории git.

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

Создать бэкэнд

  1. Перейдите на страницу App Hosting в консоли Firebase:

Нулевое состояние консоли App Hosting с кнопкой «Начать»

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

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

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

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

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

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

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

Реализовать функции входа и выхода

  1. В файле 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.
  2. Откройте страницу App Hosting в консоли Firebase и дождитесь завершения развертывания нового приложения.
  3. Проверьте новое поведение аутентификации:
    1. Обновите страницу веб-приложения в браузере. Ваше отображаемое имя отобразится в заголовке.
    2. Выйдите из системы и войдите снова. Вы можете повторить этот шаг для разных пользователей.
    3. Необязательно: Щёлкните правой кнопкой мыши по веб-приложению, выберите «Просмотреть исходный код страницы » и найдите отображаемое имя. Оно отображается в исходном HTML-коде, возвращаемом сервером.

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

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

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

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

  1. Войдите в веб-приложение, если вы ещё этого не сделали. Затем выберите 2cf67d488d8e6332.png > Добавить примеры ресторанов .
  2. В консоли Firebase на странице базы данных Firestore выберите restaurants . Вы увидите документы верхнего уровня в коллекции ресторанов, каждый из которых представляет один ресторан.
  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.
  2. Откройте страницу App Hosting в консоли Firebase и дождитесь завершения развертывания нового приложения.
  3. Обновите страницу в веб-приложении. Изображения ресторанов отображаются на странице в виде плиток.

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

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

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

  1. В веб-приложении откройте DevTools и отключите JavaScript .

Отключить JavaScipt в DevTools

  1. Обновите веб-приложение. Список ресторанов всё ещё загружается. Информация о ресторанах возвращается в ответе сервера. При включённом JavaScript информация о ресторанах обрабатывается клиентским JavaScript-кодом.
  2. В DevTools снова включите 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);
  });
}

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

  1. Создайте коммит с сообщением «Слушайте обновления ресторана в реальном времени» и отправьте его в свой репозиторий GitHub.
  2. Откройте страницу App Hosting в консоли 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 для обработки отправки формы отзыва, выполните следующие действия:

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

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

  1. В файле src/app/actions.js замените функцию handleReviewFormSubmission() следующим кодом:
// This is a next.js server action, which is an alpha feature, so
// use with caution.
// https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
export async function handleReviewFormSubmission(data) {
        const { app } = await getAuthenticatedAppForUser();
        const db = getFirestore(app);

        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.
  2. Откройте страницу App Hosting в консоли Firebase и дождитесь завершения развертывания нового приложения.
  3. Обновите веб-приложение и выберите ресторан на домашней странице.
  4. На странице ресторана нажмите 3e19beef78bb0d0e.png .
  5. Выберите рейтинг (звезды).
  6. Написать отзыв.
  7. Нажмите «Отправить» . Ваш отзыв появится в верхней части списка отзывов.
  8. В Cloud Firestore найдите на панели «Добавить документ» документ ресторана, который вы просматривали, и выберите его.
  9. На панели «Начать сбор» выберите «Рейтинги» .
  10. На панели «Добавить документ» найдите документ для проверки, чтобы убедиться, что он был вставлен должным образом.

Документы в эмуляторе Firestore

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.
  2. Откройте страницу App Hosting в консоли 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. В поле «Поиск проектов Google Cloud» выберите свой проект Firebase. Каждый проект Firebase поддерживается проектом Google Cloud.
  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 и доступен вашему серверу App Hosting.

Реализовать компонент резюме обзора

  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://firebase.google.com/docs/genkit/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.
  3. Откройте страницу App Hosting в консоли Firebase и дождитесь завершения развертывания нового приложения.
  4. Откройте страницу ресторана. Вверху вы увидите краткое изложение всех отзывов на странице.
  5. Добавьте новый отзыв и обновите страницу. Вы увидите изменение в кратком содержании.

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

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

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