Full-Stack-Next.js-Anwendung mit Cloud SQL for PostgreSQL und dem Cloud SQL Node.js-Connector in Cloud Run bereitstellen

Full-Stack-Next.js-Anwendung mit Cloud SQL for PostgreSQL und dem Cloud SQL Node.js-Connector in Cloud Run bereitstellen

Informationen zu diesem Codelab

subjectZuletzt aktualisiert: März 28, 2025
account_circleVerfasst von Luke Schlangen

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 mit dem Cloud SQL Node.js-Connector eine Next.js-Anwendung in Cloud Run mit einer Cloud SQL for PostgreSQL-Datenbank verbinden.

Aufgaben in diesem Lab:

  • Cloud SQL for PostgreSQL-Instanz erstellen
  • Anwendung in Cloud Run bereitstellen, die eine Verbindung zu Ihrer Cloud SQL-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 \
  sqladmin.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. Dienstkonto einrichten

Erstellen und konfigurieren Sie ein Google Cloud-Dienstkonto, das von Cloud Run verwendet werden soll, damit es die richtigen Berechtigungen zum Herstellen einer Verbindung zu Cloud SQL hat.

  1. Führen Sie den Befehl gcloud iam service-accounts create so aus, um ein neues Dienstkonto zu erstellen:
    gcloud iam service-accounts create quickstart-service-account \
     
    --display-name="Quickstart Service Account"
  2. Führen Sie den Befehl „gcloud projects add-iam-policy-binding“ wie unten beschrieben aus, um dem Google Cloud-Dienstkonto, das Sie gerade erstellt haben, die Rolle Cloud SQL-Client hinzuzufügen.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.client"
  3. Führen Sie den Befehl „gcloud projects add-iam-policy-binding“ wie unten beschrieben aus, um dem Google Cloud-Dienstkonto, das Sie gerade erstellt haben, die Rolle Cloud SQL-Instanznutzer hinzuzufügen.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.instanceUser"
  4. Führen Sie den Befehl „gcloud projects add-iam-policy-binding“ wie unten beschrieben aus, um dem gerade erstellten Google Cloud-Dienstkonto die Rolle Logautor hinzuzufügen.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"

7. Cloud SQL-Datenbank erstellen

  1. Befehl gcloud sql instances create ausführen, um eine Cloud SQL-Instanz zu erstellen
    gcloud sql instances create quickstart-instance \
        --database-version=POSTGRES_14 \
        --cpu=4 \
        --memory=16GB \
        --region=us-central1 \
        --database-flags=cloudsql.iam_authentication=on

Die Verarbeitung dieses Befehls kann einige Minuten dauern.

  1. Führen Sie den Befehl gcloud sql databases create aus, um eine Cloud SQL-Datenbank in der quickstart-instance zu erstellen.
    gcloud sql databases create quickstart_db \
       
    --instance=quickstart-instance
  2. Erstellen Sie einen PostgreSQL-Datenbanknutzer für das zuvor erstellte Dienstkonto, um auf die Datenbank zuzugreifen.
    gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
        --instance=quickstart-instance \
        --type=cloud_iam_service_account

8. 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 pg und die Cloud SQL Node.js-Connector-Bibliothek, um mit der PostgreSQL-Datenbank zu interagieren.
    npm install pg @google-cloud/cloud-sql-connector google-auth-library
  2. Installieren Sie @types/pg als Entwicklungsabhängigkeit, um eine TypeScript Next.js-Anwendung zu verwenden.
    npm install --save-dev @types/pg
  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 pg from 'pg';
    import { AuthTypes, Connector } from '@google-cloud/cloud-sql-connector';
    import { GoogleAuth } from 'google-auth-library';
    const auth = new GoogleAuth();

    const { Pool } = pg;

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

    const projectId = await auth.getProjectId();

    const connector = new Connector();
    const clientOpts = await connector.getOptions({
     
    instanceConnectionName: `${projectId}:us-central1:quickstart-instance`,
     
    authType: AuthTypes.IAM,
    });

    const pool = new Pool({
     
    ...clientOpts,
     
    user: `quickstart-service-account@${projectId}.iam`,
     
    database: 'quickstart_db',
    });

    const tableCreationIfDoesNotExist = async () => {
     
    await pool.query(`CREATE TABLE IF NOT EXISTS tasks (
         
    id SERIAL NOT NULL,
         
    created_at timestamp NOT NULL,
         
    status VARCHAR(255) NOT NULL default 'IN_PROGRESS',
         
    title VARCHAR(1024) NOT NULL,
         
    PRIMARY KEY (id)
       
    );`);
    }

    // CREATE
    export async function addNewTaskToDatabase(newTask: string) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(`INSERT INTO tasks(created_at, status, title) VALUES(NOW(), 'IN_PROGRESS', $1)`, [newTask]);
     
    return;
    }

    // READ
    export async function getTasksFromDatabase() {
     
    await tableCreationIfDoesNotExist();
     
    const { rows } = await pool.query(`SELECT id, created_at, status, title FROM tasks ORDER BY created_at DESC LIMIT 100`);
     
    return rows;
    }

    // UPDATE
    export async function updateTaskInDatabase(task: Task) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(
       
    `UPDATE tasks SET status = $1, title = $2 WHERE id = $3`,
       
    [task.status, task.title, task.id]
     
    );
     
    return;
    }

    // DELETE
    export async function deleteTaskFromDatabase(taskId: string) {
     
    await tableCreationIfDoesNotExist();
     
    await pool.query(`DELETE FROM tasks WHERE id = $1`, [taskId]);
     
    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.

9. Anwendung in Cloud Run bereitstellen

  1. Führen Sie den folgenden Befehl aus, um die Anwendung in Cloud Run bereitzustellen:
    gcloud run deploy to-do-tracker \
        --region=us-central1 \
        --source=. \
        --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --allow-unauthenticated
  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.

10. 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