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

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

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

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

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

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

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

  • Учетная запись GitHub
  • Знание Next.js и JavaScript.

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

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

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

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

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

Эта лаборатория кода предоставляет начальную кодовую базу приложения и использует интерфейс командной строки Firebase.

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

Исходный код codelab можно найти по адресу 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. Используйте пакет giget npm, чтобы получить только папку nextjs-start :
    npx giget@latest gh:firebase/friendlyeats-web/nextjs-start#master . --install
    
  3. Отслеживайте изменения локально с помощью git:
    git init
    
    git commit -a -m "codelab starting point"
    
    git branch -M main
    
  4. Создайте новый репозиторий GitHub: https://github.com/new . Назовите это как угодно.
    1. GitHub предоставит вам новый URL-адрес репозитория, который выглядит как https://github.com/ / .git https://github.com/ / .git https://github.com/ / .git или git@github.com: / .git git@github.com: / .git git@github.com: / .git . Скопируйте этот URL.
  5. Отправьте локальные изменения в новый репозиторий GitHub. Выполните следующую команду, заменив URL-адрес вашего репозитория заполнитель.
    git remote add origin <your-repository-url>
    
    git push -u origin main
    
  6. Теперь вы должны увидеть стартовый код в своем репозитории GitHub.

Установите или обновите интерфейс командной строки Firebase

Выполните следующую команду, чтобы убедиться, что у вас установлен интерфейс командной строки Firebase и его версия 13.9.0 или выше:

firebase --version

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

npm install -g firebase-tools@latest

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

Войдите в Firebase

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

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

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

Создать проект Firebase

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

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

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

Чтобы обновить проект до плана 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 перейдите к Authentication .
  2. Нажмите «Начать» .
  3. В столбце «Дополнительные поставщики» нажмите Google > Включить .
  4. В текстовом поле Общедоступное имя проекта введите запоминающееся имя, например My Next.js app .
  5. В раскрывающемся списке «Электронная почта поддержки проекта» выберите свой адрес электронной почты.
  6. Нажмите Сохранить .

Настройте Cloud Firestore

  1. В консоли Firebase перейдите к Firestore .
  2. Нажмите Создать базу данных > Далее > Запустить в тестовом режиме > Далее .
    Позже в этой лабораторной работе вы добавите правила безопасности для защиты ваших данных. Не распространяйте и не публикуйте приложение без добавления правил безопасности для вашей базы данных.
  3. Используйте местоположение по умолчанию или выберите местоположение по вашему выбору.
    Для реального приложения вам нужно выбрать местоположение, наиболее близкое к вашим пользователям. Обратите внимание, что это местоположение нельзя изменить позже, и оно также автоматически станет местоположением вашего сегмента Cloud Storage по умолчанию (следующий шаг).
  4. Нажмите Готово .

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

  1. В консоли Firebase перейдите к Storage .
  2. Нажмите «Начало работы» > «Начать в тестовом режиме» > «Далее» .
    Позже в этой лабораторной работе вы добавите правила безопасности для защиты ваших данных. Не распространяйте и не публикуйте приложение без добавления правил безопасности для сегмента хранилища.
  3. Местоположение вашей корзины уже должно быть выбрано (из-за настройки Firestore на предыдущем шаге).
  4. Нажмите Готово .

4. Просмотрите начальную кодовую базу

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

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

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

Папки и файлы

Описание

src/components

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

src/lib

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

src/lib/firebase

Код, специфичный для Firebase, и конфигурация Firebase

public

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

src/app

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

src/app/restaurant

Обработчик маршрута API

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

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

Ложные данные

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

5. Создайте серверную часть хостинга приложений.

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

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

Развертывание правил безопасности

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

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

Добавьте конфигурацию Firebase в код вашего веб-приложения.

  1. В консоли Firebase перейдите к настройкам проекта .
  2. На панели установки и конфигурации SDK нажмите «Добавить приложение» и щелкните значок скобок кода. для регистрации нового веб-приложения.
  3. В конце процесса создания веб-приложения скопируйте переменную firebaseConfig , а также ее свойства и их значения.
  4. Откройте файл apphosting.yaml в редакторе кода и заполните значения переменных среды значениями конфигурации из консоли Firebase.
  5. В файле замените существующие свойства теми, которые вы скопировали.
  6. Сохраните файл.

Создать серверную часть

  1. Перейдите на страницу хостинга приложений в консоли Firebase:

Нулевое состояние консоли хостинга приложений с кнопкой «Начать».

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

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

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

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

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

  1. В файле src/lib/firebase/auth.js замените функции onAuthStateChanged , signInWithGoogle и signOut следующим кодом:
export function onAuthStateChanged(cb) {
	return _onAuthStateChanged(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

Описание

GoogleAuthProvider

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

signInWithPopup

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

auth.signOut

Выполняет выход пользователя.

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

  1. Создайте коммит с сообщением о коммите «Добавление аутентификации Google» и отправьте его в свой репозиторий GitHub. 1. Откройте страницу хостинга приложений в консоли Firebase и дождитесь завершения нового развертывания.
  2. В веб-приложении обновите страницу и нажмите «Войти через Google» . Веб-приложение не обновляется, поэтому неясно, удалось ли войти в систему.

Отправить состояние аутентификации на сервер

Чтобы передать состояние аутентификации на сервер, мы будем использовать сервис-воркера . Замените функции fetchWithFirebaseHeaders и getAuthIdToken следующим кодом:

async function fetchWithFirebaseHeaders(request) {
  const app = initializeApp(firebaseConfig);
  const auth = getAuth(app);
  const installations = getInstallations(app);
  const headers = new Headers(request.headers);
  const [authIdToken, installationToken] = await Promise.all([
    getAuthIdToken(auth),
    getToken(installations),
  ]);
  headers.append("Firebase-Instance-ID-Token", installationToken);
  if (authIdToken) headers.append("Authorization", `Bearer ${authIdToken}`);
  const newRequest = new Request(request, { headers });
  return await fetch(newRequest);
}

async function getAuthIdToken(auth) {
  await auth.authStateReady();
  if (!auth.currentUser) return;
  return await getIdToken(auth.currentUser);
}

Чтение состояния аутентификации на сервере

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

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

export async function getAuthenticatedAppForUser() {
  const idToken = headers().get("Authorization")?.split("Bearer ")[1];
  console.log('firebaseConfig', JSON.stringify(firebaseConfig));
  const firebaseServerApp = initializeServerApp(
    firebaseConfig,
    idToken
      ? {
          authIdToken: idToken,
        }
      : {}
  );

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

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

Подписаться на изменения аутентификации

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

  1. Перейдите к файлу src/components/Header.jsx .
  2. Замените функцию useUserSession следующим кодом:
function useUserSession(initialUser) {
	// The initialUser comes from the server via a server component
	const [user, setUser] = useState(initialUser);
	const router = useRouter();

	// Register the service worker that sends auth state back to server
	// The service worker is built with npm run build-service-worker
	useEffect(() => {
		if ("serviceWorker" in navigator) {
			const serializedFirebaseConfig = encodeURIComponent(JSON.stringify(firebaseConfig));
			const serviceWorkerUrl = `/auth-service-worker.js?firebaseConfig=${serializedFirebaseConfig}`
		
		  navigator.serviceWorker
			.register(serviceWorkerUrl)
			.then((registration) => console.log("scope is: ", registration.scope));
		}
	  }, []);

	useEffect(() => {
		const unsubscribe = onAuthStateChanged((authUser) => {
			setUser(authUser)
		})

		return () => unsubscribe()
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	useEffect(() => {
		onAuthStateChanged((authUser) => {
			if (user === undefined) return

			// refresh when user changed to ease testing
			if (user?.email !== authUser?.email) {
				router.refresh()
			}
		})
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [user])

	return user;
}

Этот код использует перехватчик состояния React для обновления пользователя, когда функция onAuthStateChanged указывает, что произошло изменение состояния аутентификации.

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

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

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

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

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

  1. Создайте коммит с сообщением «Показать состояние входа» и отправьте его в свой репозиторий GitHub.
  2. Откройте страницу хостинга приложений в консоли Firebase и дождитесь завершения нового развертывания.
  3. Проверьте новое поведение аутентификации:
    1. В браузере обновите веб-приложение. Ваше отображаемое имя появится в заголовке.
    2. Выйдите из системы и войдите снова. Страница обновляется в режиме реального времени без обновления страницы. Вы можете повторить этот шаг с разными пользователями.
    3. Необязательно: щелкните веб-приложение правой кнопкой мыши, выберите «Просмотреть источник страницы» и найдите отображаемое имя. Он появляется в необработанном исходном HTML-коде, возвращаемом с сервера.

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

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

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

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

  1. В веб-приложении выберите 2cf67d488d8e6332.png >Добавьте образцы ресторанов .
  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.
  2. Откройте страницу хостинга приложений в консоли 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(() => {
        const unsubscribe = getRestaurantsSnapshot(data => {
                setRestaurants(data);
        }, filters);

        return () => {
                unsubscribe();
        };
}, [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);

	const unsubscribe = 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);
	});

	return unsubscribe;
}

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

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

  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. Откройте страницу хостинга приложений в консоли 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);
        setRestaurant({ ...restaurant, 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. Откройте страницу хостинга приложений в консоли Firebase и дождитесь завершения нового развертывания.
  3. В веб-приложении убедитесь, что вы вошли в систему, и выберите ресторан.
  4. Нажмите 7067eb41fea41ff0.png и загрузите изображение из вашей файловой системы. Ваше изображение покидает вашу локальную среду и загружается в облачное хранилище. Изображение появляется сразу после его загрузки.
  5. Перейдите в облачное хранилище для Firebase.
  6. Перейдите в папку, представляющую ресторан. Загруженное вами изображение существует в папке.

6cf3f9e2303c931c.png

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

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

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

  1. Чтобы использовать API Gemini, вам понадобится ключ API. Создайте ключ в Google AI Studio .
  2. Хостинг приложений интегрируется с Cloud Secret Manager, что позволяет вам безопасно хранить конфиденциальные значения, такие как ключи API:
    1. В терминале выполните команду для создания нового секрета:
    firebase apphosting:secrets:set gemini-api-key
    
    1. Когда будет предложено ввести секретное значение, скопируйте и вставьте ключ Gemini API из Google AI Studio.
    2. Когда вас спросят, следует ли добавить новый секрет в apphosting.yaml , введите Y , чтобы принять его.

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

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

  1. В src/components/Reviews/ReviewSummary.jsx замените функцию GeminiSummary следующим кодом:
    export async function GeminiSummary({ restaurantId }) {
        const { firebaseServerApp } = await getAuthenticatedAppForUser();
        const reviews = await getReviewsByRestaurantId(
            getFirestore(firebaseServerApp),
            restaurantId
        );
    
        const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
        const model = genAI.getGenerativeModel({ model: "gemini-pro"});
    
        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 {
            const result = await model.generateContent(prompt);
            const response = await result.response;
            const text = response.text();
    
            return (
                <div className="restaurant__review_summary">
                    <p>{text}</p>
                    <p> Summarized with Gemini</p>
                </div>
            );
        } catch (e) {
            console.error(e);
            return <p>Error contacting Gemini</p>;
        }
    }
    
  2. Создайте коммит с сообщением «Используйте AI для обобщения обзоров» и отправьте его в свой репозиторий GitHub.
  3. Откройте страницу хостинга приложений в консоли Firebase и дождитесь завершения нового развертывания.
  4. Откройте страницу ресторана. Вверху вы должны увидеть краткое изложение всех отзывов на странице в одно предложение.
  5. Добавьте новый отзыв и обновите страницу. Вы должны увидеть изменение сводки.

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

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

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