Implantar um aplicativo full stack do Next.js no Cloud Run com o Firestore usando o SDK Admin do Node.js

Implantar um aplicativo full stack do Next.js no Cloud Run com o Firestore usando o SDK Admin do Node.js

Sobre este codelab

subjectÚltimo abr. 1, 2025 atualizado
account_circleEscrito por Luke Schlangen

1. Visão geral

O Cloud Run é uma plataforma totalmente gerenciada que permite executar seu código diretamente na infraestrutura escalonável do Google. Este codelab vai demonstrar como conectar um aplicativo Next.js no Cloud Run a um banco de dados do Firestore usando o SDK Admin do Node.js.

Neste laboratório, você vai aprender a:

  • Crie um banco de dados Firestore
  • Implantar um aplicativo no Cloud Run que se conecte ao seu banco de dados do Firestore

2. Pré-requisitos

  1. Se você ainda não tem uma Conta do Google, crie uma.
    • Use uma conta pessoal em vez de uma conta de trabalho ou escolar. As contas escolares e de trabalho podem ter restrições que impedem a ativação das APIs necessárias para este laboratório.

3. Configurar o projeto

  1. Faça login no Console do Google Cloud.
  2. Ative o faturamento no Console do Cloud.
    • A conclusão deste laboratório deve custar menos de US $1 em recursos do Cloud.
    • Siga as etapas no final deste laboratório para excluir recursos e evitar cobranças.
    • Novos usuários estão qualificados para o teste sem custo financeiro de US$300.
  3. Crie um novo projeto ou reutilize um projeto atual.

4. Abrir editor do Cloud Shell

  1. Acesse o editor do Cloud Shell.
  2. Se o terminal não aparecer na parte de baixo da tela, abra-o:
    • Clique no menu de navegação Ícone do menu de navegação.
    • Clique em Terminal.
    • Clique em Novo Terminal.Abrir um novo terminal no editor do Cloud Shell
  3. No terminal, defina seu projeto com este comando:
    • Formato:
      gcloud config set project [PROJECT_ID]
    • Exemplo:
      gcloud config set project lab-project-id-example
    • Se você não souber o ID do projeto:
      • É possível listar todos os IDs de projetos com:
        gcloud projects list | awk '/PROJECT_ID/{print $2}'
      Definir o ID do projeto no terminal do Editor do Cloud Shell
  4. Se for necessário autorizar, clique em Autorizar para continuar. Clique para autorizar o Cloud Shell
  5. Você vai receber esta mensagem:
    Updated property [core/project].
    
    Se você receber uma WARNING e receber uma solicitação Do you want to continue (Y/N)?, provavelmente inseriu o ID do projeto incorretamente. Pressione N, pressione Enter e tente executar o comando gcloud config set project novamente.

5. Ativar APIs

No terminal, ative as APIs:

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

Se for necessário autorizar, clique em Autorizar para continuar. Clique para autorizar o Cloud Shell

Esse comando pode levar alguns minutos para ser concluído, mas, no final, vai gerar uma mensagem de sucesso semelhante a esta:

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

6. Criar um banco de dados do Firestore

  1. Execute o comando gcloud firestore databases create para criar um banco de dados do Firestore.
    gcloud firestore databases create --location=nam5

7. Preparar a inscrição

Prepare um aplicativo Next.js que responda a solicitações HTTP.

  1. Para criar um novo projeto Next.js chamado task-app, use o comando:
    npx --yes create-next-app@15.2.4 task-app \
      --ts \
      --eslint \
      --tailwind \
      --no-src-dir \
      --turbopack \
      --app \
      --no-import-alias
  2. Altere o diretório para task-app:
    cd task-app
  1. Instale o firebase-admin para interagir com o banco de dados do Firestore.
    npm install firebase-admin
  1. Abra o arquivo actions.ts no editor do Cloud Shell:
    cloudshell edit app/actions.ts
    Um arquivo vazio vai aparecer na parte de cima da tela. É aqui que você pode editar o arquivo actions.ts. Mostre que o código vai na parte de cima da tela
  2. Copie o código abaixo e cole no arquivo actions.ts aberto:
    '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. Abra o arquivo page.tsx no editor do Cloud Shell:
    cloudshell edit app/page.tsx
    Um arquivo vai aparecer na parte de cima da tela. É aqui que você pode editar o arquivo page.tsx. Mostre que o código vai na parte de cima da tela
  2. Exclua o conteúdo do arquivo page.tsx.
  3. Copie o código abaixo e cole no arquivo page.tsx aberto:
    '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>
      );
    }

O aplicativo está pronto para ser implantado.

8. Implantar o aplicativo no Cloud Run

  1. Execute o comando abaixo para implantar seu aplicativo no Cloud Run:
    gcloud run deploy helloworld \
      --region=us-central1 \
      --source=.
  2. Se necessário, pressione Y e Enter para confirmar que você quer continuar:
    Do you want to continue (Y/n)? Y
    

Após alguns minutos, o aplicativo vai fornecer um URL para você acessar.

Navegue até o URL para conferir o aplicativo em ação. O app de tarefas vai aparecer sempre que você visitar o URL ou atualizar a página.

9. Parabéns

Neste laboratório, você aprendeu a:

  • Criar uma instância do Cloud SQL para PostgreSQL
  • Implantar um aplicativo no Cloud Run que se conecte ao seu banco de dados do Cloud SQL

Limpar

O Cloud SQL não tem um nível sem custo financeiro e vai cobrar se você continuar usando. Você pode excluir seu projeto do Cloud para evitar cobranças adicionais.

O Cloud Run não gera custos quando o serviço não está em uso, mas você ainda pode receber cobranças pelo armazenamento da imagem do contêiner no Artifact Registry. A exclusão do projeto do Cloud interrompe o faturamento de todos os recursos usados nesse projeto.

Se quiser, exclua o projeto:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Você também pode excluir recursos desnecessários do disco do cloudshell. Você pode:

  1. Exclua o diretório do projeto do codelab:
    rm -rf ~/task-app
  2. Aviso: Esta próxima ação não pode ser desfeita. Se você quiser excluir tudo no Cloud Shell para liberar espaço, exclua seu diretório principal inteiro. Confira se tudo o que você quer manter está salvo em outro lugar.
    sudo rm -rf $HOME