Разверните полнофункциональное приложение Next.js в Cloud Run с Firestore с помощью Node.js Admin SDK.

Разверните полнофункциональное приложение Next.js в Cloud Run с Firestore с помощью Node.js Admin SDK.

О практической работе

subjectПоследнее обновление: апр. 11, 2025
account_circleАвторы: Luke Schlangen

1. Обзор

Cloud Run — это полностью управляемая платформа, позволяющая запускать код непосредственно поверх масштабируемой инфраструктуры Google. В этой лаборатории кода будет показано, как подключить приложение Next.js в Cloud Run к базе данных Firestore с помощью Node.js Admin SDK.

В этой лабораторной работе вы научитесь:

  • Создайте базу данных Firestore
  • Разверните приложение в Cloud Run, которое подключается к вашей базе данных Firestore.

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

  1. Если у вас еще нет учетной записи Google, вам необходимо создать учетную запись Google .
    • Используйте личную учетную запись вместо рабочей или учебной учетной записи. Рабочие и учебные учетные записи могут иметь ограничения, не позволяющие вам включить API, необходимые для этой лабораторной работы.

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

  1. Войдите в Google Cloud Console .
  2. Включите биллинг в Cloud Console.
    • Завершение этой лабораторной работы должно стоить менее 1 доллара США в облачных ресурсах.
    • Вы можете выполнить действия, описанные в конце этого практического занятия, чтобы удалить ресурсы и избежать дальнейших расходов.
    • Новые пользователи имеют право на бесплатную пробную версию стоимостью 300 долларов США .
  3. Создайте новый проект или повторно используйте существующий проект.

4. Открыть редактор Cloud Shell

  1. Перейдите в редактор Cloud Shell.
  2. Если терминал не отображается в нижней части экрана, откройте его:
    • Нажмите на гамбургер-меню Значок меню гамбургера
    • Нажмите Терминал
    • Нажмите «Новый терминал». Откройте новый терминал в редакторе Cloud Shell.
  3. В терминале настройте свой проект с помощью этой команды:
    • Формат:
      gcloud config set project [PROJECT_ID]
    • Пример:
      gcloud config set project lab-project-id-example
    • Если вы не можете вспомнить идентификатор своего проекта:
      • Вы можете перечислить все идентификаторы своих проектов с помощью:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
      Установите идентификатор проекта в терминале редактора Cloud Shell
  4. Если будет предложено авторизоваться, нажмите «Авторизовать» , чтобы продолжить. Нажмите, чтобы авторизовать Cloud Shell
  5. Вы должны увидеть это сообщение:
    Updated property [core/project].
    
    Если вы видите WARNING и вас спрашивают Do you want to continue (Y/N)? , то, вероятно, вы неправильно ввели идентификатор проекта. Нажмите N , нажмите Enter и попробуйте еще раз запустить команду gcloud config set project .

5. Включить API

В терминале включите API:

gcloud services enable \
  firestore.googleapis.com \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

Если будет предложено авторизоваться, нажмите «Авторизовать» , чтобы продолжить. Нажмите, чтобы авторизовать Cloud Shell

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

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

6. Создать базу данных Firestore

  1. Запустите команду gcloud firestore databases create , чтобы создать базу данных Firestore.
    gcloud firestore databases create --location=nam5

7. Подготовить заявку

Подготовьте приложение Next.js, отвечающее на HTTP-запросы.

  1. Чтобы создать новый проект Next.js с именем task-app , используйте команду:
    npx --yes create-next-app@15.2.4 task-app \
      --ts \
      --eslint \
      --tailwind \
      --no-src-dir \
      --turbopack \
      --app \
      --no-import-alias
  2. Измените каталог на task-app :
    cd task-app
  1. Установите firebase-admin для взаимодействия с базой данных Firestore.
    npm install firebase-admin
  1. Откройте файл actions.ts в редакторе Cloud Shell:
    cloudshell edit app/actions.ts
    Пустой файл должен появиться в верхней части экрана. Здесь вы можете редактировать файл actions.ts . Покажите, что код находится в верхней части экрана.
  2. Скопируйте следующий код и вставьте его в открывшийся файл actions.ts :
    'use server'
    import { initializeApp, applicationDefault, getApps } from 'firebase-admin/app';
    import { getFirestore } from 'firebase-admin/firestore';
    const credential = applicationDefault();

    // Only initialize app if it does not already exist
    if (getApps().length === 0) {
      initializeApp({ credential });
    }

    const db = getFirestore();
    const tasksRef = db.collection('tasks');

    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
      createdAt: number;
    };

    // CREATE
    export async function addNewTaskToDatabase(newTask: string) {
      await tasksRef.doc().create({
        title: newTask,
        status: 'IN_PROGRESS',
        createdAt: Date.now(),
      });
      return;
    }

    // READ
    export async function getTasksFromDatabase() {
      const snapshot = await tasksRef.orderBy('createdAt', 'desc').limit(100).get();
      const tasks = await snapshot.docs.map(doc => ({
        id: doc.id,
        title: doc.data().title,
        status: doc.data().status,
        createdAt: doc.data().createdAt,
      }));
      return tasks;
    }

    // UPDATE
    export async function updateTaskInDatabase(task: Task) {
      await tasksRef.doc(task.id).set(task);
      return;
    }

    // DELETE
    export async function deleteTaskFromDatabase(taskId: string) {
      await tasksRef.doc(taskId).delete();
      return;
    }
  1. Откройте файл page.tsx в редакторе Cloud Shell:
    cloudshell edit app/page.tsx
    Существующий файл теперь должен появиться в верхней части экрана. Здесь вы можете редактировать файл page.tsx . Покажите, что код находится в верхней части экрана.
  2. Удалите существующее содержимое файла page.tsx .
  3. Скопируйте следующий код и вставьте его в открывшийся файл page.tsx :
    'use client'
    import React, { useEffect, useState } from "react";
    import { addNewTaskToDatabase, getTasksFromDatabase, deleteTaskFromDatabase, updateTaskInDatabase } from "./actions";

    type Task = {
      id: string;
      title: string;
      status: 'IN_PROGRESS' | 'COMPLETE';
      createdAt: number;
    };

    export default function Home() {
      const [newTaskTitle, setNewTaskTitle] = useState('');
      const [tasks, setTasks] = useState<Task[]>([]);

      async function getTasks() {
        const updatedListOfTasks = await getTasksFromDatabase();
        setTasks(updatedListOfTasks);
      }

      useEffect(() => {
        getTasks();
      }, []);

      async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        await addNewTaskToDatabase(newTaskTitle);
        await getTasks();
        setNewTaskTitle('');
      };

      async function updateTask(task: Task, newTaskValues: Partial<Task>) {
        await updateTaskInDatabase({ ...task, ...newTaskValues });
        await getTasks();
      }

      async function deleteTask(taskId: string) {
        await deleteTaskFromDatabase(taskId);
        await getTasks();
      }

      return (
        <main className="p-4">
          <h2 className="text-2xl font-bold mb-4">To Do List</h2>
          <div className="flex mb-4">
            <form onSubmit={handleSubmit} className="flex mb-8">
              <input
                type="text"
                placeholder="New Task Title"
                value={newTaskTitle}
                onChange={(e) => setNewTaskTitle(e.target.value)}
                className="flex-grow border border-gray-400 rounded px-3 py-2 mr-2 bg-inherit"
              />
              <button
                type="submit"
                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-nowrap"
              >
                Add New Task
              </button>
            </form>
          </div>
          <table className="w-full">
            <tbody>
              {tasks.map(function (task) {
                const isComplete = task.status === 'COMPLETE';
                return (
                  <tr key={task.id} className="border-b border-gray-200">
                    <td className="py-2 px-4">
                      <input
                        type="checkbox"
                        checked={isComplete}
                        onChange={() => updateTask(task, { status: isComplete ? 'IN_PROGRESS' : 'COMPLETE' })}
                        className="transition-transform duration-300 ease-in-out transform scale-100 checked:scale-125 checked:bg-green-500"
                      />
                    </td>
                    <td className="py-2 px-4">
                      <span
                        className={`transition-all duration-300 ease-in-out ${isComplete ? 'line-through text-gray-400 opacity-50' : 'opacity-100'}`}
                      >
                        {task.title}
                      </span>
                    </td>
                    <td className="py-2 px-4">
                      <button
                        onClick={() => deleteTask(task.id)}
                        className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded float-right"
                      >
                        Delete
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </main>
      );
    }

Теперь приложение готово к развертыванию.

8. Разверните приложение в Cloud Run

  1. Выполните команду ниже, чтобы развернуть приложение в Cloud Run:
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=.
  2. При появлении запроса нажмите Y и Enter , чтобы подтвердить, что вы хотите продолжить:
    Do you want to continue (Y/n)? Y
    

Через несколько минут приложение должно предоставить вам URL-адрес для посещения.

Перейдите по URL-адресу, чтобы увидеть свое приложение в действии. Каждый раз, когда вы посещаете URL-адрес или обновляете страницу, вы увидите приложение задач.

9. Поздравления

В ходе этой лабораторной работы вы научились делать следующее:

  • Создайте экземпляр Cloud SQL для PostgreSQL.
  • Разверните приложение в Cloud Run, которое подключается к вашей базе данных Cloud SQL.

Очистить

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

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

Если хотите, удалите проект:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Вы также можете удалить ненужные ресурсы с диска CloudShell. Ты можешь:

  1. Удалите каталог проекта codelab:
    rm -rf ~/task-app
  2. Предупреждение! Следующее действие невозможно отменить! Если вы хотите удалить все в Cloud Shell, чтобы освободить место, вы можете удалить весь домашний каталог . Будьте осторожны, чтобы все, что вы хотите сохранить, сохранялось где-то еще.
    sudo rm -rf $HOME

Продолжайте учиться