Integrar o Firebase a um app Next.js

1. Antes de começar

Neste codelab, você aprenderá a integrar o Firebase a um app da Web Next.js chamado Friendly Eats, que é um site de avaliações de restaurantes.

App da Web Friendly Eats

O app da Web completo oferece recursos úteis que demonstram como o Firebase pode ajudar você a criar apps Next.js. Esses recursos incluem o seguinte:

  • Build e implantação automáticos:este codelab usa o Firebase App Hosting para criar e implantar automaticamente seu código Next.js sempre que você envia para uma ramificação configurada.
  • Login e logout:o app da Web concluído permite que você faça login com o Google e saia. O login e a persistência do usuário são gerenciados inteiramente pelo Firebase Authentication.
  • Imagens: o app da Web concluído permite que os usuários que fizeram login façam upload de imagens de restaurantes. Os recursos de imagem são armazenados no Cloud Storage para Firebase. O SDK do Firebase para JavaScript fornece um URL público para as imagens enviadas. Esse URL público é armazenado no documento do restaurante relevante no Cloud Firestore.
  • Avaliações: o app da Web concluído permite que os usuários conectados postem avaliações de restaurantes com uma nota e uma mensagem de texto. As informações das avaliações são armazenadas no Cloud Firestore.
  • Filtros: o app da Web concluído permite que os usuários que fizeram login filtrem a lista de restaurantes por categoria, local e preço. Também é possível personalizar o método de classificação usado. Os dados são acessados do Cloud Firestore, e as consultas do Firestore são aplicadas com base nos filtros usados.

Pré-requisitos

  • Uma conta no GitHub
  • Conhecimento de Next.js e JavaScript.

Neste curso, você vai aprender a:

Pré-requisitos

  • Git
  • Uma versão estável recente do Node.js
  • Um navegador da sua escolha, como o Google Chrome
  • Ambiente de desenvolvimento com um editor de código e um terminal
  • Uma Conta do Google para a criação e o gerenciamento do seu projeto do Firebase
  • A capacidade de fazer upgrade do seu projeto do Firebase para o plano de preços Blaze

2. Configurar o ambiente de desenvolvimento e o repositório do GitHub

Este codelab fornece a base de código inicial do app e depende da CLI do Firebase.

Crie um repositório do GitHub

O código-fonte do codelab está disponível em https://github.com/firebase/friendlyeats-web. O repositório contém projetos de exemplo para várias plataformas. No entanto, este codelab usa apenas o diretório nextjs-start. Anote os seguintes diretórios:

* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.

Copie a pasta nextjs-start para seu próprio repositório:

  1. Usando um terminal, crie uma pasta no computador e mude para o novo diretório:
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. Use o pacote npm giget para buscar apenas a pasta nextjs-start:
    npx giget@latest "gh:firebase/friendlyeats-web/nextjs-start#master" . --install
    
  3. Rastreie as mudanças localmente com o git:
    git init
    
    git add .
    
    git commit -m "Codelab starting point"
    
    git branch -M main
    
  4. Crie um repositório do GitHub: https://github.com/new. Dê o nome que quiser.
  5. Dependendo de como você autentica o GitHub (HTTPS ou SSH), copie o novo URL criado para você:
    • https://github.com/<USER_NAME>/<REPOSITORY_NAME>.git ou
    • git@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
  6. Envie as mudanças locais para o novo repositório do GitHub executando o seguinte comando. Substitua o URL real do repositório pelo marcador de posição <REPOSITORY_URL>.
    git remote add origin <REPOSITORY_URL>
    
    git push -u origin main
    
  7. Agora você vai ver o código inicial no repositório do GitHub.

Instalar ou atualizar a CLI do Firebase

Execute o seguinte comando para verificar se você tem a CLI do Firebase instalada e se ela é a v14.1.0 ou mais recente:

firebase --version

Se você vir uma versão mais antiga ou não tiver a CLI do Firebase instalada, execute o comando de instalação:

npm install -g firebase-tools@latest

Se não for possível instalar a CLI do Firebase devido a erros de permissão, consulte a documentação do npm ou use outra opção de instalação.

Fazer login no Firebase

  1. Execute o seguinte comando para fazer login na CLI do Firebase:
    firebase login
    
  2. Para que o Firebase colete dados, insira Y ou N.
  3. No navegador, selecione sua Conta do Google e clique em Permitir.

3. Configurar seu projeto do Firebase

Nesta seção, você vai configurar um projeto do Firebase e associar um app da Web do Firebase a ele. Você também vai configurar os serviços do Firebase usados pelo app da Web de exemplo.

Criar um projeto do Firebase

  1. Faça login no console do Firebase usando a mesma Conta do Google da etapa anterior.
  2. Clique no botão para criar um projeto e insira um nome (por exemplo, FriendlyEats Codelab).
  3. Clique em Continuar.
  4. Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
  5. (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
  6. Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
  7. Clique em Criar projeto, aguarde o provisionamento e clique em Continuar.

Fazer upgrade do plano de preços do Firebase

Para usar o Firebase App Hosting e o Cloud Storage para Firebase, seu projeto do Firebase precisa estar no plano de preços de pagamento por uso (Blaze), o que significa que ele está vinculado a uma conta do Cloud Billing.

  • Uma conta do Cloud Billing exige uma forma de pagamento, como cartão de crédito.
  • Se você ainda não conhece o Firebase e o Google Cloud, confira se tem qualificação para receber um crédito de US$300 e uma conta de teste sem custos financeiros do Cloud Billing.
  • Se você estiver fazendo este codelab como parte de um evento, pergunte ao organizador se há créditos do Cloud disponíveis.

Para fazer upgrade do seu projeto para o plano Blaze, siga estas etapas:

  1. No console do Firebase, selecione Fazer upgrade do seu plano.
  2. Selecione o plano Blaze. Siga as instruções na tela para vincular uma conta do Cloud Billing ao seu projeto.
    Se você precisou criar uma conta do Cloud Billing como parte desse upgrade, talvez seja necessário voltar para o fluxo de upgrade no console do Firebase para concluir o processo.

Adicionar um app da Web ao seu projeto do Firebase

  1. Navegue até a Visão geral do projeto no seu projeto do Firebase, clique em Adicionar app e em Web.
  2. Na caixa de texto Apelido do app, digite um apelido fácil de lembrar, como My Next.js app.
  3. Deixe a caixa de seleção Também configurar o Firebase Hosting para este app desmarcada.
  4. Clique em Registrar app > Continuar para o console.

Configurar serviços do Firebase no console do Firebase

Configurar o Authentication

  1. No painel esquerdo do Console do Firebase, expanda Build e selecione Autenticação.
  2. Clique em Começar.
  3. Na coluna Provedores de login, clique em Google > Ativar.
  4. Na caixa de texto Nome voltado ao público do projeto, digite um nome fácil de lembrar, como My Next.js app.
  5. No menu suspenso E-mail de suporte do projeto, selecione seu endereço de e-mail.
  6. Clique em Salvar.

Configurar o Cloud Firestore

  1. No painel à esquerda do console do Firebase, expanda Build e selecione Banco de dados do Firestore.
  2. Clique em Criar banco de dados.
  3. Escolha Edição Standard e clique em Próxima.
  4. Não mude o ID do banco de dados. Deixe-o definido como (default).
  5. Selecione um local para o banco de dados e clique em Próxima.
    No caso de apps reais, escolha um local próximo aos usuários.
  6. Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
    Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao seu banco de dados.
  7. Clique em Criar.

Configurar o Cloud Storage para Firebase

  1. No painel à esquerda do console do Firebase, expanda Build e selecione Storage.
  2. Clique em Começar.
  3. Selecione um local para seu bucket de armazenamento padrão.
    Os buckets em US-WEST1, US-CENTRAL1 e US-EAST1 podem aproveitar o nível"Sempre sem custo financeiro" do Google Cloud Storage. Os buckets em todos os outros locais seguem os preços e usos do Google Cloud Storage.
  4. Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
    Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao bucket do Storage.
  5. Clique em Criar.

Implantar regras de segurança

O código já tem conjuntos de regras de segurança para o Firestore e o Cloud Storage para Firebase. Após a implantação das regras de segurança, os dados no banco de dados e no bucket ficam mais bem protegidos contra uso indevido.

  1. No terminal, configure a CLI para usar o projeto do Firebase que você criou antes:
    firebase use --add
    
    Quando um alias for solicitado, digite friendlyeats-codelab.
  2. Para implantar essas regras de segurança (e os índices que serão necessários mais tarde), execute este comando no terminal:
    firebase deploy --only firestore,storage
    
  3. Se aparecer a pergunta "Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", pressione Enter para selecionar Sim.

4. Revisar a base de código inicial

Nesta seção, você vai revisar algumas áreas da base de código inicial do app às quais vai adicionar funcionalidades neste codelab.

Estrutura de pastas e arquivos

A tabela a seguir contém uma visão geral da estrutura de pastas e arquivos do app:

Pastas e arquivos

Descrição

src/components

Componentes do React para filtros, cabeçalhos, detalhes de restaurantes e avaliações

src/lib

Funções utilitárias que não estão necessariamente vinculadas ao React ou Next.js.

src/lib/firebase

Código específico e configuração do Firebase

public

Recursos estáticos no app da Web, como ícones

src/app

Roteamento com o roteador de apps Next.js

package.json e package-lock.json

Dependências do projeto com npm

next.config.js

Configuração específica do Next.js (as ações do servidor estão ativadas)

jsconfig.json

Configuração do serviço de linguagem JavaScript

Componentes do servidor e do cliente

O app é um app da Web Next.js que usa o Roteador de apps. A renderização do servidor é usada em todo o app. Por exemplo, o arquivo src/app/page.js é um componente do servidor responsável pela página principal. O arquivo src/components/RestaurantListings.jsx é um componente do cliente indicado pela diretiva "use client" no início do arquivo.

Declarações de importação

Você vai notar instruções de importação como estas:

import RatingPicker from "@/src/components/RatingPicker.jsx";

O app usa o símbolo @ para evitar caminhos de importação relativos pesados e é possível graças a aliases de caminho.

APIs específicas do Firebase

Todo o código da API do Firebase é encapsulado no diretório src/lib/firebase. Os componentes individuais do React importam as funções encapsuladas do diretório src/lib/firebase, em vez de importar as funções do Firebase diretamente.

Dados simulados

Os dados simulados de restaurantes e avaliações estão no arquivo src/lib/randomData.js. Os dados desse arquivo são reunidos no código do arquivo src/lib/fakeRestaurants.js.

5. Criar um back-end do App Hosting

Nesta seção, você vai configurar um back-end do App Hosting para monitorar uma ramificação no seu repositório git.

Ao final desta seção, você terá um back-end do App Hosting conectado ao seu repositório no GitHub, que vai recriar e lançar automaticamente uma nova versão do app sempre que você enviar um novo commit para a ramificação main.

Criar um back-end

  1. Acesse a página App Hosting no console do Firebase.
  2. Clique em Começar para iniciar o fluxo de criação do back-end.
  3. Escolha uma região. Para um app real, escolha a região mais próxima dos usuários.
  4. Siga as instruções na etapa Importar um repositório do GitHub para configurar a autenticação do GitHub.
  5. Em Repositório, selecione Conceder acesso a um novo repositório no GitHub e siga as instruções para ativar o acesso ao repositório do GitHub que você criou antes.
  6. Clique em Atualizar lista, selecione seu repositório e clique em Próxima.
  7. Defina as configurações de implantação:
    1. Defina a ramificação ativa como main.
    2. Mantenha o diretório raiz como /.
    3. Ative os lançamentos automáticos.
  8. Nomeie o back-end friendlyeats-codelab e clique em Próxima.
  9. Em Associar um app da Web do Firebase, escolha Selecionar um app da Web do Firebase e selecione o app que você adicionou na lista.
  10. Clique em Concluir e implantar. Você vai ser redirecionado para uma nova página em que poderá conferir o status do seu novo back-end do App Hosting.
  11. Clique em Visualizar para conferir mais informações sobre sua implantação do App Hosting, incluindo status de lançamento, registros e detalhes de uso.
  12. Depois que a implantação for concluída, clique para abrir o URL do site em Domínios. Isso pode levar alguns minutos para começar a funcionar devido à propagação do DNS.
  13. Ops. Ao carregar a página, você vai encontrar uma mensagem de erro que diz "Erro de aplicativo: ocorreu uma exceção do lado do servidor. Consulte os registros do servidor para mais informações".
  14. No console do Firebase, verifique a guia Registros do back-end do App Hosting. Você vai encontrar um registro "Error: not implemented". Vamos corrigir isso na próxima etapa, quando adicionarmos a autenticação.

Você implantou o app da Web inicial. Sempre que você enviar um novo commit para a ramificação main do seu repositório do GitHub, um novo build e lançamento vão começar no console do Firebase, e seu site será atualizado automaticamente após a conclusão do lançamento.

6. Adicionar autenticação ao app da Web

Nesta seção, você vai adicionar uma autenticação ao app da Web para fazer login nele.

Adicione um domínio autorizado

O Firebase Authentication só aceita solicitações de login de domínios permitidos. Aqui, vamos adicionar o domínio do back-end do App Hosting à lista de domínios aprovados no seu projeto.

  1. Abra a página App Hosting e clique em Ver abaixo do site implantado para acessar a página Visão geral. Copie o nome de domínio do back-end do App Hosting.
  2. Acesse a guia Configurações de autenticação e escolha o projeto a que você quer adicionar um domínio autorizado. Em seguida, localize e clique na seção Domínios autorizados.
  3. Clique no botão Adicionar domínio.
  4. Insira o domínio do back-end do App Hosting.
  5. Clique em Adicionar.

Implementar as funções de login e logout

No arquivo src/lib/firebase/auth.js, substitua as funções onAuthStateChanged, onIdTokenChanged, signInWithGoogle e signOut pelo seguinte código:

export function onAuthStateChanged(cb) {
  return _onAuthStateChanged(auth, cb);
}

export function onIdTokenChanged(cb) {
  return _onIdTokenChanged(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);
  }
}

Esse código usa as seguintes APIs do Firebase:

API Firebase

Descrição

auth.onAuthStateChanged

Adiciona um observador para mudanças no estado de login do usuário.

auth.onIdTokenChanged

Adiciona um observador para mudanças no token de ID do usuário.

GoogleAuthProvider

Cria uma instância do provedor de autenticação do Google.

signInWithPopup

Inicia um fluxo de autenticação baseado em caixa de diálogo.

auth.signOut

Desconecta o usuário.

No arquivo src/components/Header.jsx, o código já invoca as funções signInWithGoogle e signOut.

Enviar o estado de autenticação para o servidor

Para transmitir o estado de autenticação ao servidor, vamos usar cookies. Sempre que o estado de autenticação mudar no cliente, vamos atualizar o cookie __session.

Em src/components/Header.jsx, substitua a função useUserSession pelo seguinte código:

function useUserSession(initialUser) {
  useEffect(() => {
    return onIdTokenChanged(async (user) => {
      if (user) {
        const idToken = await user.getIdToken();
        await setCookie("__session", idToken);
      } else {
        await deleteCookie("__session");
      }
      if (initialUser?.uid === user?.uid) {
        return;
      }
      window.location.reload();
    });
  }, [initialUser]);

  return initialUser;
}

Ler o estado de autenticação no servidor

Vamos usar o FirebaseServerApp para espelhar o estado de autenticação do cliente no servidor.

Abra src/lib/firebase/serverApp.js e substitua a função getAuthenticatedAppForUser:

export async function getAuthenticatedAppForUser() {
  const authIdToken = (await cookies()).get("__session")?.value;

  // Firebase Server App is a new feature in the JS SDK that allows you to
  // instantiate the SDK with credentials retrieved from the client & has
  // other affordances for use in server environments.
  const firebaseServerApp = initializeServerApp(
    // https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
    initializeApp(),
    {
      authIdToken,
    }
  );

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

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

Verificar mudanças

O layout raiz no arquivo src/app/layout.js renderiza o cabeçalho e transmite o usuário, se disponível, como uma propriedade.

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

Isso significa que o componente <Header> renderiza dados do usuário, se disponíveis, durante o ambiente de execução do servidor. Se houver alguma atualização de autenticação durante o ciclo de vida da página após o carregamento inicial da página, ela será processada pelo gerenciador onAuthStateChanged.

Agora é hora de lançar um novo build e verificar o que você criou.

  1. Crie uma confirmação com a mensagem "Adicionar autenticação" e envie para o repositório do GitHub.
    git add .
    
    git commit -m "Add authentication"
    
    git push
    
  2. Abra a página de hospedagem de apps e, quando o novo lançamento estiver concluído, clique no URL do site para abri-lo.
  3. Teste de autenticação:
    1. Faça login com sua Conta do Google e verifique se o nome de exibição aparece no cabeçalho depois do login.
    2. Saia e faça login novamente. Repita essa etapa com diferentes usuários.
    3. Opcional: clique com o botão direito do mouse no app da Web, selecione Conferir código-fonte da página e pesquise o nome de exibição. Ele aparece no código-fonte HTML bruto retornado do servidor.

7. Acessar informações do restaurante

O app da Web inclui dados simulados de restaurantes e avaliações.

Adicionar um ou mais restaurantes

Para inserir dados simulados de restaurantes no seu banco de dados local do Cloud Firestore, siga estas etapas:

  1. Faça login no web app, caso ainda não tenha feito isso. Em seguida, selecione 2cf67d488d8e6332.png> Adicionar restaurantes de exemplo. Nenhum restaurante aparece no app da Web Friendly Eats porque ainda não configuramos o código de busca de dados. Isso será corrigido na próxima etapa.
  2. No console do Firebase, na página Banco de dados do Firestore, selecione restaurantes. Você encontrará os documentos de nível superior na coleção do restaurante. Cada um deles representa um restaurante.
  3. Clique em alguns documentos para explorar as propriedades de um documento de restaurante.

Mostrar a lista de restaurantes

Seu banco de dados do Cloud Firestore agora tem restaurantes que o app da Web Next.js pode exibir.

Para definir o código de busca de dados, siga estas etapas:

  1. No arquivo src/app/page.js, encontre o componente de servidor <Home /> e revise a chamada para a função getRestaurants, que recupera uma lista de restaurantes no ambiente de execução do servidor. Implemente a função getRestaurants nas etapas a seguir.
  2. No arquivo src/lib/firebase/firestore.js, substitua as funções applyQueryFilters e getRestaurants pelo seguinte código:
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. Crie um commit com a mensagem "Read the list of restaurants from Firestore" e envie para o repositório do GitHub.
    git add .
    
    git commit -m "Read the list of restaurants from Firestore"
    
    git push
    
  2. Abra a página do App Hosting no console do Firebase e aguarde a conclusão do novo lançamento.
  3. No app da Web, atualize a página. As imagens do restaurante aparecem como blocos na página.

Verificar se as listas do restaurante são carregadas no ambiente de execução do servidor

Usando o framework Next.js, pode não ser óbvio quando os dados são carregados no ambiente de execução do servidor ou do lado do cliente.

Para verificar se as fichas de restaurantes são carregadas no ambiente de execução do servidor, siga estas etapas:

  1. No app da Web, abra o DevTools e desative o JavaScript.

Desativar o JavaScipt no DevTools

  1. Atualize o app da Web. As listas dos restaurantes ainda são carregadas. As informações do restaurante são retornadas na resposta do servidor. Quando o JavaScript está ativado, as informações do restaurante são hidratadas com o código JavaScript do lado do cliente.
  2. No DevTools, reative o JavaScript.

Detectar atualizações de restaurantes com listeners de snapshots do Cloud Firestore

Na seção anterior, você soube como o conjunto inicial de restaurantes foi carregado do arquivo src/app/page.js. O arquivo src/app/page.js é um componente do servidor e é renderizado no servidor, incluindo o código de busca de dados do Firebase.

O arquivo src/components/RestaurantListings.jsx é um componente do cliente e pode ser configurado para hidratar a marcação renderizada pelo servidor.

Se quiser configurar o arquivo src/components/RestaurantListings.jsx para hidratar a marcação renderizada pelo servidor, siga estas etapas:

  1. No arquivo src/components/RestaurantListings.jsx, observe o seguinte código, que já está escrito para você:
useEffect(() => {
    return getRestaurantsSnapshot((data) => {
      setRestaurants(data);
    }, filters);
  }, [filters]);

Esse código invoca a função getRestaurantsSnapshot(), que é semelhante à função getRestaurants() implementada em uma etapa anterior. No entanto, essa função de snapshot fornece um mecanismo de callback para que o callback seja invocado sempre que uma mudança for feita na coleção do restaurante.

  1. No arquivo src/lib/firebase/firestore.js, substitua a função getRestaurantsSnapshot() pelo seguinte código:
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);

  return 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);
  });
}
  1. No arquivo src/lib/firebase/firestore.js, substitua a função getRestaurantSnapshotById() pelo seguinte código:
export function getRestaurantSnapshotById(restaurantId, cb) {
  if (!restaurantId) {
    console.log("Error: Invalid ID received: ", restaurantId);
    return;
  }

  if (typeof cb !== "function") {
    console.log("Error: The callback parameter is not a function");
    return;
  }

  const docRef = doc(db, "restaurants", restaurantId);
  return onSnapshot(docRef, (docSnap) => {
    cb({
      ...docSnap.data(),
      timestamp: docSnap.data().timestamp.toDate(),
    });
  });
}

As alterações feitas na página do banco de dados do Firestore agora são refletidas no app da Web em tempo real.

  1. Crie um commit com a mensagem "Listen for realtime restaurant updates" e envie para seu repositório do GitHub.
    git add .
    
    git commit -m "Listen for realtime restaurant updates"
    
    git push
    
  2. Abra a página do App Hosting no console do Firebase e aguarde a conclusão do novo lançamento.
  3. No app da Web, selecione 27ca5d1e8ed8adfe.png> Adicionar restaurantes de exemplo. Se a função de snapshot for implementada corretamente, os restaurantes vão aparecer em tempo real, sem precisar atualizar a página.

8. Salvar avaliações enviadas pelo usuário no app da Web

  1. No arquivo src/lib/firebase/firestore.js, substitua a função updateWithRating() pelo seguinte código:
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()),
  });
};

Esse código insere um novo documento do Firestore que representa a nova avaliação. O código também atualiza o documento atual do Firestore que representa o restaurante com números atualizados para o número de avaliações e a classificação média calculada.

  1. Substitua a função addReviewToRestaurant() por este código:
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;
  }
}

Implementar uma ação do servidor Next.js

Uma ação do servidor Next.js oferece uma API conveniente para acessar dados de formulário, como data.get("text"), para acessar o valor de texto do payload de envio de formulário.

Se quiser usar uma ação do servidor do Next.js para processar o envio do formulário de avaliação, siga estas etapas:

  1. No arquivo src/components/ReviewDialog.jsx, encontre o atributo action no elemento <form>.
<form
  action={handleReviewFormSubmission}
  onSubmit={() => {
    handleClose();
  }}
>

O valor do atributo action se refere a uma função que você vai implementar na próxima etapa.

  1. No arquivo src/app/actions.js, substitua a função handleReviewFormSubmission() pelo seguinte código:
export async function handleReviewFormSubmission(data) {
  const { firebaseServerApp } = await getAuthenticatedAppForUser();
  const db = getFirestore(firebaseServerApp);

  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"),
  });
}

Adicionar avaliações de um restaurante

Você implementou a compatibilidade com envios de avaliação, então agora pode verificar se as avaliações foram inseridas corretamente no Cloud Firestore.

Para adicionar uma avaliação e verificar se ela está inserida no Cloud Firestore, siga estas etapas:

  1. Crie um commit com a mensagem "Allow users to submit restaurant reviews" e envie para seu repositório do GitHub.
    git add .
    
    git commit -m "Allow users to submit restaurant reviews"
    
    git push
    
  2. Abra a página do App Hosting no console do Firebase e aguarde a conclusão do novo lançamento.
  3. Atualize o app da Web e selecione um restaurante na página inicial.
  4. Na página do restaurante, clique em 3e19beef78bb0d0e.png.
  5. Selecione uma nota.
  6. Escreva uma avaliação.
  7. Clique em Enviar. Sua avaliação vai aparecer no topo da lista.
  8. No Cloud Firestore, pesquise o documento do restaurante que você avaliou no painel Adicionar documento e selecione-o.
  9. No painel Iniciar coleta, selecione classificações.
  10. No painel Adicionar documento, encontre o documento da sua avaliação para verificar se ele foi inserido conforme o esperado.

9. Salvar arquivos enviados pelo usuário no app da Web

Nesta seção, você vai adicionar uma funcionalidade para substituir a imagem associada a um restaurante quando estiver conectado. Você faz upload da imagem para o Firebase Storage e atualiza o URL da imagem no documento do Cloud Firestore que representa o restaurante.

Para salvar arquivos enviados por usuários no app da Web, siga estas etapas:

  1. No arquivo src/components/Restaurant.jsx, observe o código executado quando o usuário faz upload de um arquivo:
async function handleRestaurantImage(target) {
  const image = target.files ? target.files[0] : null;
  if (!image) {
    return;
  }

  const imageURL = await updateRestaurantImage(id, image);
  setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}

Nenhuma mudança é necessária nessa função, mas você vai implementar o comportamento da função updateRestaurantImage() nas etapas a seguir.

  1. No arquivo src/lib/firebase/storage.js, substitua as funções updateRestaurantImage() e uploadImage() pelo seguinte código:
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);
}

A função updateRestaurantImageReference() já foi implementada para você. Essa função atualiza um documento de restaurante no Cloud Firestore com um URL de imagem atualizado.

Verificar a funcionalidade de upload de imagem

Para verificar se o upload da imagem é o esperado, siga estas etapas:

  1. Crie um commit com a mensagem "Allow users to change each restaurants' photo" e envie para seu repositório do GitHub.
    git add .
    
    git commit -m "Allow users to change each restaurants' photo"
    
    git push
    
  2. Abra a página do App Hosting no console do Firebase e aguarde a conclusão do novo lançamento.
  3. No app da Web, verifique se você fez login e selecione um restaurante.
  4. Clique em 7067eb41fea41ff0.png e faça upload de uma imagem do seu sistema de arquivos. A imagem sai do ambiente local e é carregada no Cloud Storage. A imagem aparece imediatamente após o upload.
  5. Acesse o Cloud Storage para Firebase.
  6. Navegue até a pasta que representa o restaurante. A imagem que você enviou existe na pasta.

6cf3f9e2303c931c.png

10. Resumir avaliações de restaurantes com a IA generativa

Nesta seção, você vai adicionar um recurso de resumo de avaliações para que um usuário entenda rapidamente o que todos pensam de um restaurante sem precisar ler todas as avaliações.

Armazenar uma chave de API do Gemini no Cloud Secret Manager

  1. Para usar a API Gemini, você precisará de uma chave de API. Acesse o Google AI Studio e clique em Criar chave de API.
  2. Dê o nome que quiser à chave. Se o projeto não estiver listado em Escolher um projeto importado, clique em Importar projeto, marque o projeto na lista e clique em Importar. Por fim, selecione-o em Escolher um projeto importado e clique em Criar uma chave.
  3. O App Hosting se integra ao Cloud Secret Manager para permitir que você armazene valores sensíveis, como chaves de API, com segurança:
    1. Em um terminal, execute o comando para criar um novo secret:
    firebase apphosting:secrets:set GEMINI_API_KEY
    
    1. Quando o valor secreto for solicitado, copie e cole sua chave da API Gemini do Google AI Studio.
    2. Quando perguntado se o novo secret é para produção ou teste local, escolha "Produção".
    3. Quando perguntado se você quer conceder acesso para que a conta de serviço do back-end possa acessar o secret, selecione "Sim".
    4. Quando for perguntado se o novo secret deve ser adicionado a apphosting.yaml, digite Y para aceitar.

Sua chave de API do Gemini agora está armazenada com segurança no Cloud Secret Manager e pode ser acessada pelo back-end do App Hosting.

Implementar o componente de resumo da avaliação

  1. Em src/components/Reviews/ReviewSummary.jsx, substitua a função GeminiSummary pelo seguinte código:
    export async function GeminiSummary({ restaurantId }) {
      const { firebaseServerApp } = await getAuthenticatedAppForUser();
      const reviews = await getReviewsByRestaurantId(
        getFirestore(firebaseServerApp),
        restaurantId
      );
    
      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 {
        if (!process.env.GEMINI_API_KEY) {
          // Make sure GEMINI_API_KEY environment variable is set:
          // https://firebase.google.com/docs/genkit/get-started
          throw new Error(
            'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"'
          );
        }
    
        // Configure a Genkit instance.
        const ai = genkit({
          plugins: [googleAI()],
          model: gemini20Flash, // set default model
        });
        const { text } = await ai.generate(prompt);
    
        return (
          <div className="restaurant__review_summary">
            <p>{text}</p>
            <p> Summarized with Gemini</p>
          </div>
        );
      } catch (e) {
        console.error(e);
        return <p>Error summarizing reviews.</p>;
      }
    }
    
  2. Crie um commit com a mensagem "Use AI to summarize reviews" e envie para seu repositório do GitHub.
    git add .
    
    git commit -m "Use AI to summarize reviews"
    
    git push
    
  3. Abra a página do App Hosting no console do Firebase e aguarde a conclusão do novo lançamento.
  4. Abra a página de um restaurante. Na parte de cima, você vai encontrar um resumo de uma frase com todas as avaliações na página.
  5. Adicione uma nova avaliação e atualize a página. O resumo vai mudar.

11. Cancelar a publicação do site do App Hosting

Depois de concluir este codelab, se você não for continuar usando o app, cancele a publicação dele para garantir que ninguém acesse seus recursos do Firestore, do Storage e do Gemini. Você pode publicar novamente a qualquer momento.

Para cancelar a publicação de um site do App Hosting:

  1. Abra o App Hosting no Console do Firebase.
  2. Localize o back-end do app e clique em Ver.
  3. Na seção Informações do back-end, ao lado de Domínios, clique em Gerenciar. Isso carrega a página Domínios.
  4. Ao lado do seu domínio, clique no ícone Mais (três pontos verticais), escolha Desativar domínio e clique em Desativar para confirmar.

12. Conclusão

Parabéns! Você aprendeu a usar o Firebase para adicionar recursos e funcionalidades a um app Next.js. Especificamente, você usou o seguinte:

Saiba mais