Full-Stack-Next.js-Anwendung mit dem Node.js Admin SDK in Cloud Run mit Firestore bereitstellen

1. Übersicht

Cloud Run ist eine vollständig verwaltete Plattform, mit der Sie Ihren Code direkt auf der skalierbaren Infrastruktur von Google ausführen können. In diesem Codelab wird gezeigt, wie Sie eine Next.js-Anwendung in Cloud Run mithilfe des Node.js Admin SDK mit einer Firestore-Datenbank verbinden.

Aufgaben in diesem Lab:

  • Firestore-Datenbank erstellen
  • Anwendung in Cloud Run bereitstellen, die eine Verbindung zu Ihrer Firestore-Datenbank herstellt

2. Vorbereitung

  1. Wenn Sie noch kein Google-Konto haben, müssen Sie ein Google-Konto erstellen.
    • Sie verwenden ein privates Konto anstelle eines Arbeits- oder Schulkontos. Für geschäftliche und schulische Konten gelten möglicherweise Einschränkungen, die Sie daran hindern, die für dieses Lab erforderlichen APIs zu aktivieren.

3. Projekt einrichten

  1. Melden Sie sich in der Google Cloud Console an.
  2. Aktivieren Sie die Abrechnung in der Cloud Console.
    • Die Kosten für die Durchführung dieses Labs sollten unter 1 $liegen.
    • Sie können die Schritte am Ende dieses Labs ausführen, um Ressourcen zu löschen und weitere Kosten zu vermeiden.
    • Neue Nutzer können das kostenlose Testabo mit einem Guthaben von 300$ nutzen.
  3. Erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes.

4. Cloud Shell-Editor öffnen

  1. Rufen Sie den Cloud Shell-Editor auf.
  2. Wenn das Terminal nicht unten auf dem Bildschirm angezeigt wird, öffnen Sie es:
    • Klicke auf das Dreistrich-Menü Dreistrich-Menüsymbol.
    • Klicken Sie auf Terminal.
    • Klicken Sie auf Neues TerminalNeues Terminal im Cloud Shell-Editor öffnen.
  3. Legen Sie das Projekt im Terminal mit diesem Befehl fest:
    • Format:
      gcloud config set project [PROJECT_ID]
      
    • Beispiel:
      gcloud config set project lab-project-id-example
      
    • Wenn Sie sich Ihre Projekt-ID nicht merken können:
      • So listen Sie alle Ihre Projekt-IDs auf:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
        
      Projekt-ID im Cloud Shell-Editor-Terminal festlegen
  4. Wenn Sie dazu aufgefordert werden, klicken Sie auf Autorisieren, um fortzufahren. Klicken Sie, um Cloud Shell zu autorisieren.
  5. Es sollte folgende Meldung angezeigt werden:
    Updated property [core/project].
    
    Wenn du die Meldung WARNING und die Frage Do you want to continue (Y/N)? siehst, hast du wahrscheinlich die Projekt-ID falsch eingegeben. Drücken Sie N, Enter und versuchen Sie noch einmal, den Befehl gcloud config set project auszuführen.

5. APIs aktivieren

Aktivieren Sie im Terminal die APIs:

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

Wenn Sie dazu aufgefordert werden, klicken Sie auf Autorisieren, um fortzufahren. Klicken Sie, um Cloud Shell zu autorisieren.

Die Verarbeitung dieses Befehls kann einige Minuten dauern. Sie sollten aber schließlich eine Meldung ähnlich der folgenden erhalten:

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

6. Firestore-Datenbank erstellen

  1. Befehl gcloud firestore databases create ausführen, um eine Firestore-Datenbank zu erstellen
    gcloud firestore databases create --location=nam5
    

7. Anwendung vorbereiten

Bereiten Sie eine Next.js-Anwendung vor, die auf HTTP-Anfragen reagiert.

  1. Verwenden Sie den folgenden Befehl, um ein neues Next.js-Projekt mit dem Namen task-app zu erstellen:
    npx --yes create-next-app@15.2.4 task-app \
      --ts \
      --eslint \
      --tailwind \
      --no-src-dir \
      --turbopack \
      --app \
      --no-import-alias
    
  2. Ändern Sie das Verzeichnis in task-app:
    cd task-app
    
  1. Installieren Sie firebase-admin, um mit der Firestore-Datenbank zu interagieren.
    npm install firebase-admin
    
  1. Öffnen Sie die Datei actions.ts im Cloud Shell-Editor:
    cloudshell edit app/actions.ts
    
    Oben auf dem Bildschirm sollte jetzt eine leere Datei angezeigt werden. Hier können Sie die Datei actions.ts bearbeiten. Der Code wird oben auf dem Bildschirm angezeigt.
  2. Kopieren Sie den folgenden Code und fügen Sie ihn in die geöffnete actions.ts-Datei ein:
    '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. Öffnen Sie die Datei page.tsx im Cloud Shell-Editor:
    cloudshell edit app/page.tsx
    
    Oben auf dem Bildschirm sollte jetzt eine vorhandene Datei angezeigt werden. Hier können Sie die Datei page.tsx bearbeiten. Der Code wird oben auf dem Bildschirm angezeigt.
  2. Löschen Sie den vorhandenen Inhalt der Datei page.tsx.
  3. Kopieren Sie den folgenden Code und fügen Sie ihn in die geöffnete page.tsx-Datei ein:
    '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>
      );
    }
    

Die Anwendung kann jetzt bereitgestellt werden.

8. Anwendung in Cloud Run bereitstellen

  1. Führen Sie den folgenden Befehl aus, um die Anwendung in Cloud Run bereitzustellen:
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=.
    
  2. Wenn Sie dazu aufgefordert werden, drücken Sie Y und Enter, um zu bestätigen, dass Sie fortfahren möchten:
    Do you want to continue (Y/n)? Y
    

Nach einigen Minuten sollte die Anwendung eine URL anzeigen, die Sie aufrufen können.

Rufen Sie die URL auf, um Ihre Anwendung in Aktion zu sehen. Jedes Mal, wenn Sie die URL aufrufen oder die Seite aktualisieren, wird die Aufgaben-App angezeigt.

9. Glückwunsch

In diesem Lab haben Sie Folgendes gelernt:

  • Cloud SQL for PostgreSQL-Instanz erstellen
  • Anwendung in Cloud Run bereitstellen, die eine Verbindung zu Ihrer Cloud SQL-Datenbank herstellt

Bereinigen

Cloud SQL ist nicht kostenlos. Wenn Sie die Plattform weiter nutzen, werden Ihnen Kosten in Rechnung gestellt. Sie können Ihr Cloud-Projekt löschen, um zusätzliche Kosten zu vermeiden.

Für Cloud Run fallen keine Kosten an, wenn der Dienst nicht verwendet wird. Allerdings können für das Speichern des Container-Images in Artifact Registry Gebühren berechnet werden. Wenn Sie Ihr Cloud-Projekt löschen, wird die Abrechnung für alle in diesem Projekt verwendeten Ressourcen beendet.

Sie können das Projekt löschen:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Sie können auch unnötige Ressourcen von Ihrem CloudShell-Laufwerk löschen. Sie haben folgende Möglichkeiten:

  1. Löschen Sie das Codelab-Projektverzeichnis:
    rm -rf ~/task-app
    
  2. Warnung! Diese nächste Aktion kann nicht rückgängig gemacht werden. Wenn Sie alles in Cloud Shell löschen möchten, um Speicherplatz freizugeben, können Sie Ihr ganzes Basisverzeichnis löschen. Achten Sie darauf, dass alles, was Sie behalten möchten, an einem anderen Ort gespeichert ist.
    sudo rm -rf $HOME
    

Weiter lernen