Criar com o Firebase Data Connect

1. Antes de começar

App FriendlyMovies

Neste codelab, você vai integrar o Firebase Data Connect a um banco de dados do Cloud SQL para criar um app da Web de análise de filmes. O app concluído mostra como o Firebase Data Connect simplifica o processo de criação de aplicativos com tecnologia SQL. Ele inclui os seguintes recursos:

  • Autenticação:implemente a autenticação personalizada para as consultas e mutações do app, garantindo que apenas usuários autorizados possam interagir com seus dados.
  • Esquema do GraphQL:crie e gerencie suas estruturas de dados usando um esquema flexível do GraphQL adaptado às necessidades de um app da Web de análise de filmes.
  • Consultas e mutações SQL:extraia, atualize e gerencie dados no Cloud SQL usando consultas e mutações com tecnologia GraphQL.
  • Pesquisa avançada com correspondência parcial de string:use filtros e opções de pesquisa para encontrar filmes com base em campos como título, descrição ou tags.
  • (Opcional) Integração da pesquisa de vetores:adicione a funcionalidade de pesquisa de conteúdo usando a pesquisa de vetores do Firebase Data Connect para oferecer uma experiência do usuário rica com base nas entradas e preferências.

Pré-requisitos

Você vai precisar de conhecimentos básicos de JavaScript.

O que você aprenderá

  • Configure o Firebase Data Connect com emuladores locais.
  • Projete um esquema de dados usando o Data Connect e o GraphQL.
  • Crie e teste várias consultas e mutações para um app de análise de filmes.
  • Saiba como o Firebase Data Connect gera e usa o SDK no app.
  • Implante seu esquema e gerencie o banco de dados com eficiência.

O que é necessário

2. Configurar o ambiente de desenvolvimento

Nesta etapa do codelab, você vai aprender a configurar o ambiente para começar a criar seu app de análise de filmes usando o Firebase Data Connect.

  1. Clone o repositório do projeto e instale as dependências necessárias:
    git clone https://github.com/firebaseextended/codelab-dataconnect-web
    cd codelab-dataconnect-web
    cd ./app && npm i
    npm run dev
    
  2. Depois de executar esses comandos, abra http://localhost:5173 no navegador para conferir o app da Web em execução localmente. Ele serve como front-end para criar o app de análise de filmes e interagir com os recursos dele.93f6648a2532c606.png
  3. Abra a pasta codelab-dataconnect-web clonada usando o Visual Studio Code. É aqui que você define seu esquema, escreve consultas e testa a funcionalidade do app.
  4. Para usar os recursos do Data Connect, instale a extensão do Visual Studio do Firebase Data Connect.
    Como alternativa, você pode instalar a extensão no Visual Studio Code Marketplace ou pesquisar no VS Code.b03ee38c9a81b648.png
  5. Abra ou crie um projeto do Firebase no Console do Firebase.
  6. Conecte seu projeto do Firebase à extensão do VSCode do Firebase Data Connect. Na extensão, faça o seguinte:
    1. Clique no botão Fazer login.
    2. Clique em Conectar um projeto do Firebase e selecione seu projeto do Firebase.
    4bb2fbf8f9fac29b.png
  7. Inicie os emuladores do Firebase usando a extensão do VS Code do Firebase Data Connect:
    Clique em Start Emulators e confirme se os emuladores estão em execução no terminal.6d3d95f4cb708db1.png

3. Revisar a base de código inicial

Nesta seção, você vai conhecer as principais áreas da base de código inicial do app. Embora o app não tenha algumas funcionalidades, é útil entender a estrutura geral.

Estrutura de pastas e arquivos

As subseções a seguir fornecem uma visão geral da estrutura de pastas e arquivos do app.

O diretório dataconnect/

Contém configurações do Firebase Data Connect, conectores (que definem consultas e mutações) e arquivos de esquema.

  • schema/schema.gql: define o esquema do GraphQL
  • connector/queries.gql: consultas necessárias no app
  • connector/mutations.gql: mutações necessárias no app
  • connector/connector.yaml: arquivo de configuração para geração de SDK

O diretório app/src/

Contém a lógica do aplicativo e a interação com o Firebase Data Connect.

  • firebase.ts: configuração para se conectar a um app do Firebase no seu projeto do Firebase.
  • lib/dataconnect-sdk/: contém o SDK gerado. É possível editar o local da geração do SDK no arquivo connector/connector.yaml, e os SDKs serão gerados automaticamente sempre que você definir uma consulta ou mutação.

4. Definir um esquema para resenhas de filmes

Nesta seção, você vai definir a estrutura e as relações entre as principais entidades do aplicativo de filmes em um esquema. Entidades como Movie, User, Actor e Review são mapeadas para tabelas de banco de dados, com relacionamentos estabelecidos usando o Firebase Data Connect e as diretivas de esquema do GraphQL. Depois de implementado, o app estará pronto para processar tudo, desde a pesquisa de filmes com as melhores classificações e a filtragem por gênero até permitir que os usuários deixem avaliações, marquem favoritos, explorem filmes semelhantes ou encontrem filmes recomendados com base na entrada de texto usando a pesquisa vetorial.

Entidades e relacionamentos principais

O tipo Movie contém detalhes importantes, como título, gênero e tags, que o app usa para pesquisas e perfis de filmes. O tipo User rastreia as interações do usuário, como avaliações e adição aos favoritos. Reviews conectam usuários a filmes, permitindo que o app mostre classificações e feedback gerados pelo usuário.

As relações entre filmes, atores e usuários tornam o app mais dinâmico. A tabela de mesclagem MovieActor ajuda a mostrar detalhes do elenco e filmografias de atores. O tipo FavoriteMovie permite que os usuários marquem filmes como favoritos, para que o app possa mostrar uma lista personalizada de favoritos e destacar as opções mais populares.

Configurar a tabela Movie

O tipo Movie define a estrutura principal de uma entidade de filme, incluindo campos como title, genre, releaseYear e rating.

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type Movie
  @table {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
}

Aprendizados importantes:

  • id:um UUID exclusivo para cada filme, gerado usando @default(expr: "uuidV4()").

Configurar a tabela MovieMetadata

O tipo MovieMetadata estabelece uma relação de um para um com o tipo Movie. Ele inclui dados adicionais, como o diretor do filme.

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type MovieMetadata
  @table {
  # @ref creates a field in the current table (MovieMetadata)
  # It is a reference that holds the primary key of the referenced type
  # In this case, @ref(fields: "movieId", references: "id") is implied
  movie: Movie! @ref
  # movieId: UUID <- this is created by the above @ref
  director: String
}

Aprendizados importantes:

  • Filme! @ref: Faz referência ao tipo Movie, estabelecendo uma relação de chave estrangeira.

Configurar a tabela Actor

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type Actor @table {
  id: UUID!
  imageUrl: String!
  name: String! @col(name: "name", dataType: "varchar(30)")
}

O tipo Actor representa um ator no banco de dados de filmes, em que cada ator pode fazer parte de vários filmes, formando uma relação de muitos para muitos.

Configurar a tabela MovieActor

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type MovieActor @table(key: ["movie", "actor"]) {
  # @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
  # In this case, @ref(fields: "id") is implied
  movie: Movie!
  # movieId: UUID! <- this is created by the implied @ref, see: implicit.gql

  actor: Actor!
  # actorId: UUID! <- this is created by the implied  @ref, see: implicit.gql

  role: String! # "main" or "supporting"
}

Aprendizados importantes:

  • movie:faz referência ao tipo "Movie" e gera implicitamente uma chave estrangeira movieId: UUID.
  • actor:faz referência ao tipo de ator e gera implicitamente uma chave estrangeira actorId: UUID.
  • role:define o papel do ator no filme (por exemplo, "principal" ou "de suporte").

Configurar a tabela User

O tipo User define uma entidade do usuário que interage com filmes deixando avaliações ou adicionando filmes aos favoritos.

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type User
  @table {
  id: String! @col(name: "auth_uid")
  username: String! @col(dataType: "varchar(50)")
  # The following are generated from the @ref in the Review table
  # reviews_on_user
  # movies_via_Review
}

Configurar a tabela FavoriteMovie

O tipo FavoriteMovie é uma tabela de mesclagem que processa relacionamentos de muitos para muitos entre usuários e os filmes favoritos deles. Cada tabela vincula uma User a uma Movie.

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type FavoriteMovie
  @table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
  # @ref is implicit
  user: User!
  movie: Movie!
}

Aprendizados importantes:

  • movie:faz referência ao tipo "Movie" e gera implicitamente uma chave estrangeira movieId: UUID!.
  • user:faz referência ao tipo de usuário e gera implicitamente uma chave estrangeira userId: UUID!.

Configurar a tabela Review

O tipo Review representa a entidade de avaliação e vincula os tipos User e Movie em uma relação de muitos para muitos (um usuário pode deixar muitas avaliações, e cada filme pode ter muitas avaliações).

Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql:

type Review @table(name: "Reviews", key: ["movie", "user"]) {
  id: UUID! @default(expr: "uuidV4()")
  user: User!
  movie: Movie!
  rating: Int
  reviewText: String
  reviewDate: Date! @default(expr: "request.time")
}

Aprendizados importantes:

  • user:faz referência ao usuário que deixou a avaliação.
  • movie:faz referência ao filme que está sendo analisado.
  • reviewDate:é definida automaticamente como o horário em que a avaliação foi criada usando @default(expr: "request.time").

Campos e padrões gerados automaticamente

O esquema usa expressões como @default(expr: "uuidV4()") para gerar automaticamente IDs e carimbos de data/hora exclusivos. Por exemplo, o campo id nos tipos Movie e Review é preenchido automaticamente com um UUID quando um novo registro é criado.

Agora que o esquema está definido, seu app de filmes tem uma base sólida para a estrutura de dados e as relações.

5. Extrair os principais e os últimos filmes

App FriendlyMovies

Nesta seção, você vai inserir dados de filmes simulados nos emuladores locais e, em seguida, implementar os conectores (consultas) e o código TypeScript para chamar esses conectores no aplicativo da Web. No final, o app vai poder buscar e mostrar dinamicamente os filmes mais bem avaliados e os mais recentes diretamente do banco de dados.

Inserir dados simulados de filmes, atores e avaliações

  1. No VSCode, abra dataconnect/moviedata_insert.gql. Verifique se os emuladores na extensão do Firebase Data Connect estão em execução.
  2. Você vai encontrar um botão Run (local) na parte de cima do arquivo. Clique aqui para inserir os dados fictícios do filme no banco de dados.
    e424f75e63bf2e10.png
  3. Verifique o terminal Data Connect Execution para confirmar que os dados foram adicionados.
    e0943d7704fb84ea.png

Implementar o conector

  1. Abra dataconnect/movie-connector/queries.gql. Você vai encontrar uma consulta ListMovies básica nos comentários:
    query ListMovies @auth(level: PUBLIC) {
      movies {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
    Essa consulta busca todos os filmes e os detalhes deles (por exemplo, id, title, releaseYear). No entanto, ela não classifica os filmes.
  2. Substitua a consulta ListMovies atual pela seguinte consulta para adicionar opções de classificação e limite:
    # List subset of fields for movies
    query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) {
      movies(
        orderBy: [
          { rating: $orderByRating },
          { releaseYear: $orderByReleaseYear }
        ]
        limit: $limit
      ) {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
  3. Clique no botão Run (local) para executar a consulta no seu banco de dados local. Também é possível inserir as variáveis de consulta no painel de configuração antes da execução.
    c4d947115bb11b16.png

Aprendizados importantes:

  • movies():campo de consulta GraphQL para buscar dados de filmes do banco de dados.
  • orderByRating:parâmetro para classificar filmes por classificação (ascendente/descendente).
  • orderByReleaseYear:parâmetro para classificar filmes por ano de lançamento (ascendente/descendente).
  • limit:restringe o número de filmes retornados.

Integrar consultas no app da Web

Nesta parte do codelab, você vai usar as consultas definidas na seção anterior no seu app da Web. Os emuladores do Firebase Data Connect geram SDKs com base nas informações dos arquivos .gql (especificamente schema.gql, queries.gql, mutations.gql) e connector.yaml. Esses SDKs podem ser chamados diretamente no seu app.

  1. Em MovieService (app/src/lib/MovieService.tsx), descomente a instrução de importação na parte de cima:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    A função listMovies, o tipo de resposta ListMoviesData e o tipo enumerado OrderDirection são SDKs gerados pelos emuladores do Firebase Data Connect com base no esquema e nas consultas que você definiu anteriormente .
  2. Substitua as funções handleGetTopMovies e handleGetLatestMovies pelo seguinte código:
    // Fetch top-rated movies
    export const handleGetTopMovies = async (
      limit: number
    ): Promise<ListMoviesData["movies"] | null> => {
      try {
        const response = await listMovies({
          orderByRating: OrderDirection.DESC,
          limit,
        });
        return response.data.movies;
      } catch (error) {
        console.error("Error fetching top movies:", error);
        return null;
      }
    };
    
    // Fetch latest movies
    export const handleGetLatestMovies = async (
      limit: number
    ): Promise<ListMoviesData["movies"] | null> => {
      try {
        const response = await listMovies({
          orderByReleaseYear: OrderDirection.DESC,
          limit,
        });
        return response.data.movies;
      } catch (error) {
        console.error("Error fetching latest movies:", error);
        return null;
      }
    };
    

Aprendizados importantes:

  • listMovies:uma função gerada automaticamente que chama a consulta listMovies para extrair uma lista de filmes. Ela inclui opções para classificar por classificação ou ano de lançamento e limitar o número de resultados.
  • ListMoviesData:o tipo de resultado usado para mostrar os 10 filmes mais recentes e os mais assistidos na página inicial do app.

Veja isso na prática

Atualize o app da Web para conferir a consulta em ação. A página inicial agora mostra dinamicamente a lista de filmes, extraindo dados diretamente do seu banco de dados local. Os filmes mais bem avaliados e os mais recentes vão aparecer sem problemas, refletindo os dados que você acabou de configurar.

6. Mostrar detalhes de filmes e atores

Nesta seção, você vai implementar a funcionalidade para extrair informações detalhadas de um filme ou ator usando os IDs exclusivos. Isso envolve não apenas buscar dados das respectivas tabelas, mas também unir tabelas relacionadas para mostrar detalhes abrangentes, como críticas de filmes e filmografias de atores.

ac7fefa7ff779231.png

Implementar conectores

  1. Abra dataconnect/movie-connector/queries.gql no seu projeto.
  2. Adicione as seguintes consultas para extrair detalhes de filmes e atores:
    # Get movie by id
    query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
    movie(id: $id) {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        description
        tags
        metadata: movieMetadatas_on_movie {
          director
        }
        mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
          id
          name
          imageUrl
        }
        supportingActors: actors_via_MovieActor(
          where: { role: { eq: "supporting" } }
        ) {
          id
          name
          imageUrl
        }
        reviews: reviews_on_movie {
          id
          reviewText
          reviewDate
          rating
          user {
            id
            username
          }
        }
      }
    }
    
    # Get actor by id
    query GetActorById($id: UUID!) @auth(level: PUBLIC) {
      actor(id: $id) {
        id
        name
        imageUrl
        mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) {
          id
          title
          genre
          tags
          imageUrl
        }
        supportingActors: movies_via_MovieActor(
          where: { role: { eq: "supporting" } }
        ) {
          id
          title
          genre
          tags
          imageUrl
        }
      }
    }
    
  3. Salve as alterações e revise as consultas.

Aprendizados importantes:

  • movie()/actor(): campos de consulta do GraphQL para buscar um único filme ou ator da tabela Movies ou Actors.
  • _on_: permite o acesso direto a campos de um tipo associado que tem uma relação de chave estrangeira. Por exemplo, reviews_on_movie busca todas as avaliações relacionadas a um filme específico.
  • _via_: usado para navegar em relacionamentos muitos para muitos por uma tabela de mesclagem. Por exemplo, actors_via_MovieActor acessa o tipo Actor pela tabela de mesclagem MovieActor, e a condição where filtra atores com base no papel deles, por exemplo, "principal" ou "de apoio".

Testar a consulta inserindo dados fictícios

  1. No painel de execução do Data Connect, é possível testar a consulta inserindo IDs falsos, como:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. Clique em Run (local) para GetMovieById e extrair os detalhes sobre "Quantum Paradox" (o filme simulado ao qual o ID acima se refere).

1b08961891e44da2.png

Integrar consultas no app da Web

  1. Em MovieService (app/src/lib/MovieService.tsx), descomente as seguintes importações:
    import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
    import { GetActorByIdData, getActorById } from "@movie/dataconnect";
    
  2. Substitua as funções handleGetMovieById e handleGetActorById pelo seguinte código:
    // Fetch movie details by ID
    export const handleGetMovieById = async (
      movieId: string
    ) => {
      try {
        const response = await getMovieById({ id: movieId });
        if (response.data.movie) {
          return response.data.movie;
        }
        return null;
      } catch (error) {
        console.error("Error fetching movie:", error);
        return null;
      }
    };
    
    // Calling generated SDK for GetActorById
    export const handleGetActorById = async (
      actorId: string
    ): Promise<GetActorByIdData["actor"] | null> => {
      try {
        const response = await getActorById({ id: actorId });
        if (response.data.actor) {
          return response.data.actor;
        }
        return null;
      } catch (error) {
        console.error("Error fetching actor:", error);
        return null;
      }
    };
    

Aprendizados importantes:

  • getMovieById / getActorById: são funções geradas automaticamente que chamam as consultas definidas, recuperando informações detalhadas de um filme ou ator específico.
  • GetMovieByIdData / GetActorByIdData: são os tipos de resultado usados para mostrar detalhes de filmes e atores no app.

Veja isso na prática

Agora, acesse a página inicial do seu app da Web. Clique em um filme para conferir todos os detalhes, incluindo atores e avaliações, informações extraídas de tabelas relacionadas. Da mesma forma, clicar em um ator mostra os filmes em que ele participou.

7. Processar a autenticação do usuário

Nesta seção, você vai implementar a funcionalidade de login e logout do usuário usando o Firebase Authentication. Você também vai usar os dados do Firebase Authentication para extrair ou inserir dados de usuários diretamente no Firebase DataConnect, garantindo o gerenciamento seguro de usuários no seu app.

9890838045d5a00e.png

Implementar conectores

  1. Abra mutations.gql no dataconnect/movie-connector/.
  2. Adicione a seguinte mutação para criar ou atualizar o usuário autenticado atual:
    # Create or update the current authenticated user
    mutation UpsertUser($username: String!) @auth(level: USER) {
      user_upsert(
        data: {
          id_expr: "auth.uid"
          username: $username
        }
      )
    }
    

Aprendizados importantes:

  • id_expr: "auth.uid": usa auth.uid, que é fornecido diretamente pelo Firebase Authentication, e não pelo usuário ou pelo app, adicionando uma camada extra de segurança ao garantir que o ID do usuário seja processado de forma segura e automática.

Buscar o usuário atual

  1. Abra queries.gql no dataconnect/movie-connector/.
  2. Adicione a consulta abaixo para buscar o usuário atual:
    # Get user by ID
    query GetCurrentUser @auth(level: USER) {
      user(key: { id_expr: "auth.uid" }) {
        id
        username
        reviews: reviews_on_user {
          id
          rating
          reviewDate
          reviewText
          movie {
            id
            title
          }
        }
        favoriteMovies: favorite_movies_on_user {
          movie {
            id
            title
            genre
            imageUrl
            releaseYear
            rating
            description
            tags
            metadata: movieMetadatas_on_movie {
              director
            }
          }
        }
      }
    }
    

Aprendizados importantes:

  • auth.uid: é extraído diretamente do Firebase Authentication, garantindo o acesso seguro a dados específicos do usuário.
  • Campos _on_: esses campos representam as tabelas de mesclagem:
    • reviews_on_user: busca todas as avaliações relacionadas ao usuário, incluindo o id e o title do filme.
    • favorite_movies_on_user: recupera todos os filmes marcados como favoritos pelo usuário, incluindo informações detalhadas como genre, releaseYear, rating e metadata.

Integrar consultas no app da Web

  1. Em MovieService (app/src/lib/MovieService.tsx), remova o comentário das seguintes importações:
    import { upsertUser } from "@movie/dataconnect";
    import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
    
  2. Substitua as funções handleAuthStateChange e handleGetCurrentUser pelo seguinte código:
    // Handle user authentication state changes and upsert user
    export const handleAuthStateChange = (
      auth: any,
      setUser: (user: User | null) => void
    ) => {
      return onAuthStateChanged(auth, async (user) => {
        if (user) {
          setUser(user);
          const username = user.email?.split("@")[0] || "anon";
          await upsertUser({ username });
        } else {
          setUser(null);
        }
      });
    };
    
    // Fetch current user profile
    export const handleGetCurrentUser = async (): Promise<
      GetCurrentUserData["user"] | null
    > => {
      try {
        const response = await getCurrentUser();
        return response.data.user;
      } catch (error) {
        console.error("Error fetching user profile:", error);
        return null;
      }
    };
    

Aprendizados importantes:

  • handleAuthStateChange: essa função detecta mudanças no estado de autenticação. Quando um usuário faz login, ele define os dados do usuário e chama a mutação upsertUser para criar ou atualizar as informações do usuário no banco de dados.
  • handleGetCurrentUser: busca o perfil do usuário atual usando a consulta getCurrentUser, que recupera as avaliações e os filmes favoritos do usuário.

Veja isso na prática

Agora, clique no botão "Fazer login com o Google" na barra de navegação. Você pode fazer login usando o emulador do Firebase Authentication. Depois de fazer login, clique em "Meu perfil". Por enquanto, ele vai estar vazio, mas você já configurou a base para o processamento de dados específico do usuário no app.

8. Implementar interações do usuário

Nesta seção do codelab, você vai implementar interações do usuário no app de resenhas de filmes, permitindo que os usuários gerenciem os filmes favoritos e deixem ou excluam resenhas.

b3d0ac1e181c9de9.png

Permitir que um usuário marque um filme como favorito

Nesta seção, você vai configurar o banco de dados para permitir que os usuários marquem um filme como favorito.

Implementar conectores

  1. Abra mutations.gql no dataconnect/movie-connector/.
  2. Adicione as seguintes mutações para processar a inclusão de filmes nos favoritos:
    # Add a movie to the user's favorites list
    mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
    }
    
    # Remove a movie from the user's favorites list
    mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
    }
    
    

Aprendizados importantes:

  • userId_expr: "auth.uid": usa auth.uid, que é fornecido diretamente pelo Firebase Authentication, garantindo que apenas os dados do usuário autenticado sejam acessados ou modificados.

Verificar se um filme está marcado como favorito

  1. Abra queries.gql no dataconnect/movie-connector/.
  2. Adicione a consulta abaixo para verificar se um filme está marcado como favorito:
    query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
        movieId
      }
    }
    

Aprendizados importantes:

  • auth.uid: garante o acesso seguro a dados específicos do usuário usando o Firebase Authentication.
  • favorite_movie: verifica a tabela de mesclagem favorite_movies para saber se um filme específico foi marcado como favorito pelo usuário atual.

Integrar consultas no app da Web

  1. Em MovieService (app/src/lib/MovieService.tsx), descomente as seguintes importações:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. Substitua as funções handleAddFavoritedMovie, handleDeleteFavoritedMovie e handleGetIfFavoritedMovie pelo seguinte código:
    // Add a movie to user's favorites
    export const handleAddFavoritedMovie = async (
      movieId: string
    ): Promise<void> => {
      try {
        await addFavoritedMovie({ movieId });
      } catch (error) {
        console.error("Error adding movie to favorites:", error);
        throw error;
      }
    };
    
    // Remove a movie from user's favorites
    export const handleDeleteFavoritedMovie = async (
      movieId: string
    ): Promise<void> => {
      try {
        await deleteFavoritedMovie({ movieId });
      } catch (error) {
        console.error("Error removing movie from favorites:", error);
        throw error;
      }
    };
    
    // Check if the movie is favorited by the user
    export const handleGetIfFavoritedMovie = async (
      movieId: string
    ): Promise<boolean> => {
      try {
        const response = await getIfFavoritedMovie({ movieId });
        return !!response.data.favorite_movie;
      } catch (error) {
        console.error("Error checking if movie is favorited:", error);
        return false;
      }
    };
    

Aprendizados importantes:

  • handleAddFavoritedMovie e handleDeleteFavoritedMovie: use as mutações para adicionar ou remover um filme dos favoritos do usuário com segurança.
  • handleGetIfFavoritedMovie: usa a consulta getIfFavoritedMovie para verificar se um filme foi marcado como favorito pelo usuário.

Veja isso na prática

Agora, você pode adicionar ou remover filmes dos favoritos clicando no ícone de coração nos cards e na página de detalhes do filme. Além disso, você pode conferir seus filmes favoritos na página do seu perfil.

Permitir que os usuários deixem comentários ou os excluam

Em seguida, você vai implementar a seção para gerenciar as avaliações dos usuários no app.

Implementar conectores

Em mutations.gql (dataconnect/movie-connector/mutations.gql): adicione as seguintes mutações:

# Add a review for a movie
mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
@auth(level: USER) {
  review_insert(
    data: {
      userId_expr: "auth.uid"
      movieId: $movieId
      rating: $rating
      reviewText: $reviewText
      reviewDate_date: { today: true }
    }
  )
}

# Delete a user's review for a movie
mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
  review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}

Aprendizados importantes:

  • userId_expr: "auth.uid": garante que as avaliações sejam associadas ao usuário autenticado.
  • reviewDate_date: { today: true }: gera automaticamente a data atual da avaliação usando o DataConnect, eliminando a necessidade de entrada manual.

Integrar consultas no app da Web

  1. Em MovieService (app/src/lib/MovieService.tsx), descomente as seguintes importações:
    import { addReview, deleteReview } from "@movie/dataconnect";
    
  2. Substitua as funções handleAddReview e handleDeleteReview pelo seguinte código:
    // Add a review to a movie
    export const handleAddReview = async (
      movieId: string,
      rating: number,
      reviewText: string
    ): Promise<void> => {
      try {
        await addReview({ movieId, rating, reviewText });
      } catch (error) {
        console.error("Error adding review:", error);
        throw error;
      }
    };
    
    // Delete a review from a movie
    export const handleDeleteReview = async (movieId: string): Promise<void> => {
      try {
        await deleteReview({ movieId });
      } catch (error) {
        console.error("Error deleting review:", error);
        throw error;
      }
    };
    

Aprendizados importantes:

  • handleAddReview: chama a mutação addReview para adicionar uma avaliação ao filme especificado, vinculando-a com segurança ao usuário autenticado.
  • handleDeleteReview: usa a mutação deleteReview para remover uma avaliação de um filme feita pelo usuário autenticado.

Veja isso na prática

Agora os usuários podem deixar avaliações de filmes na página de detalhes do filme. Eles também podem conferir e excluir as avaliações na página de perfil, o que dá a eles controle total sobre as interações com o app.

9. Filtros avançados e correspondência de texto parcial

Nesta seção, você vai implementar recursos avançados de pesquisa, permitindo que os usuários pesquisem filmes com base em uma variedade de classificações e anos de lançamento, filtrem por gêneros e tags, realizem correspondência parcial de texto em títulos ou descrições e até combinem vários filtros para resultados mais precisos.

ece70ee0ab964e28.png

Implementar conectores

  1. Abra queries.gql no dataconnect/movie-connector/.
  2. Adicione a consulta a seguir para oferecer suporte a vários recursos de pesquisa:
    # Search for movies, actors, and reviews
    query SearchAll(
      $input: String
      $minYear: Int!
      $maxYear: Int!
      $minRating: Float!
      $maxRating: Float!
      $genre: String!
    ) @auth(level: PUBLIC) {
      moviesMatchingTitle: movies(
        where: {
          _and: [
            { releaseYear: { ge: $minYear } }
            { releaseYear: { le: $maxYear } }
            { rating: { ge: $minRating } }
            { rating: { le: $maxRating } }
            { genre: { contains: $genre } }
            { title: { contains: $input } }
          ]
        }
      ) {
        id
        title
        genre
        rating
        imageUrl
      }
      moviesMatchingDescription: movies(
        where: {
          _and: [
            { releaseYear: { ge: $minYear } }
            { releaseYear: { le: $maxYear } }
            { rating: { ge: $minRating } }
            { rating: { le: $maxRating } }
            { genre: { contains: $genre } }
            { description: { contains: $input } }
          ]
        }
      ) {
        id
        title
        genre
        rating
        imageUrl
      }
      actorsMatchingName: actors(where: { name: { contains: $input } }) {
        id
        name
        imageUrl
      }
      reviewsMatchingText: reviews(where: { reviewText: { contains: $input } }) {
        id
        rating
        reviewText
        reviewDate
        movie {
          id
          title
        }
        user {
          id
          username
        }
      }
    }
    

Aprendizados importantes:

  • Operador _and: combina várias condições em uma única consulta, permitindo que a pesquisa seja filtrada por vários campos, como releaseYear, rating e genre.
  • Operador contains: pesquisa correspondências de texto parcial nos campos. Nessa consulta, ele procura correspondências em title, description, name ou reviewText.
  • Cláusula where: especifica as condições para filtrar dados. Cada seção (filmes, atores, críticas) usa uma cláusula where para definir os critérios específicos da pesquisa.

Integrar consultas no app da Web

  1. Em MovieService (app/src/lib/MovieService.tsx), descomente as seguintes importações:
    import { searchAll, SearchAllData } from "@movie/dataconnect";
    
  2. Substitua a função handleSearchAll pelo seguinte código:
    // Function to perform the search using the query and filters
    export const handleSearchAll = async (
      searchQuery: string,
      minYear: number,
      maxYear: number,
      minRating: number,
      maxRating: number,
      genre: string
    ): Promise<SearchAllData | null> => {
      try {
        const response = await searchAll({
          input: searchQuery,
          minYear,
          maxYear,
          minRating,
          maxRating,
          genre,
        });
    
        return response.data;
      } catch (error) {
        console.error("Error performing search:", error);
        return null;
      }
    };
    

Aprendizados importantes:

  • handleSearchAll: essa função usa a consulta searchAll para realizar uma pesquisa com base na entrada do usuário, filtrando os resultados por parâmetros como ano, classificação, gênero e correspondências parciais de texto.

Veja isso na prática

Acesse a página "Pesquisa avançada" na barra de navegação do app da Web. Agora você pode pesquisar filmes, atores e resenhas usando vários filtros e entradas, recebendo resultados de pesquisa detalhados e personalizados.

10. Opcional: implantar na nuvem (é necessário faturamento)

Agora que você concluiu a iteração de desenvolvimento local, é hora de implantar o esquema, os dados e as consultas no servidor. Isso pode ser feito usando a extensão do Firebase Data Connect para o VS Code ou a CLI do Firebase.

Fazer upgrade do plano de preços do Firebase

Para integrar o Firebase Data Connect ao Cloud SQL para PostgreSQL, seu projeto do Firebase precisa estar no plano de preços de pagamento por uso (Blaze), ou seja, 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 custo financeiro 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 ao fluxo de upgrade no console do Firebase para concluir o upgrade.

Conectar seu app da Web ao projeto do Firebase

  1. Registre o app da Web no seu projeto do Firebase usando o console do Firebase:
    1. Abra o projeto e clique em Adicionar app.
    2. Por enquanto, ignore a configuração do SDK e a configuração, mas copie o objeto firebaseConfig gerado.
    7030822793e4d75b.png
  2. Substitua o firebaseConfig em app/src/lib/firebase.tsx pela configuração que você acabou de copiar do Console do Firebase.
    const firebaseConfig = {
      apiKey: "API_KEY",
      authDomain: "PROJECT_ID.firebaseapp.com",
      projectId: "PROJECT_ID",
      storageBucket: "PROJECT_ID.firebasestorage.app",
      messagingSenderId: "SENDER_ID",
      appId: "APP_ID"
    };
    
  3. Criar o app da Web:de volta no VS Code, na pasta app, use o Vite para criar o app da Web para a implantação de hospedagem:
    cd app
    npm run build
    

Configurar o Firebase Authentication no seu projeto do Firebase

  1. Configure o Firebase Authentication com o Login do Google.62af2f225e790ef6.png
  2. (Opcional) Permita domínios para a Autenticação do Firebase usando o console do Firebase (por exemplo, http://127.0.0.1).
    1. Nas configurações de Autenticação, acesse Domínios autorizados.
    2. Clique em "Adicionar domínio" e inclua seu domínio local na lista.

c255098f12549886.png

Implantar com a CLI do Firebase

  1. Em dataconnect/dataconnect.yaml, verifique se o ID da instância, o banco de dados e o ID do serviço correspondem ao seu projeto:
    specVersion: "v1alpha"
    serviceId: "your-service-id"
    location: "us-central1"
    schema:
      source: "./schema"
      datasource:
        postgresql:
          database: "your-database-id"
          cloudSql:
            instanceId: "your-instance-id"
    connectorDirs: ["./movie-connector"]
    
  2. Verifique se a CLI do Firebase está configurada com seu projeto:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. No terminal, execute o seguinte comando para implantar:
    firebase deploy --only dataconnect,hosting
    
  4. Execute este comando para comparar as mudanças no esquema:
    firebase dataconnect:sql:diff
    
  5. Se as mudanças forem aceitáveis, aplique-as com:
    firebase dataconnect:sql:migrate
    

Sua instância do Cloud SQL para PostgreSQL será atualizada com o esquema e os dados implantados. É possível monitorar o status no Console do Firebase.

Agora você pode acessar o app em your-project.web.app/. Além disso, você pode clicar em Run (Production) no painel do Firebase Data Connect, assim como fez com os emuladores locais, para adicionar dados ao ambiente de produção.

11. Opcional: pesquisa de vetores com o Firebase Data Connect (requer faturamento)

Nesta seção, você vai ativar a pesquisa de vetores no seu app de análise de filmes usando o Firebase Data Connect. Esse recurso permite pesquisas baseadas em conteúdo, como encontrar filmes com descrições semelhantes usando embeddings de vetor.

Para concluir esta etapa, você precisa ter concluído a última etapa deste codelab para implantar no Google Cloud.

4b5aca5a447d2feb.png

Atualizar o esquema para incluir incorporações de um campo

Em dataconnect/schema/schema.gql, adicione o campo descriptionEmbedding à tabela Movie:

type Movie
  # The below parameter values are generated by default with @table, and can be edited manually.
  @table {
  # implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
  descriptionEmbedding: Vector @col(size:768) # Enables vector search
}

Aprendizados importantes:

  • descriptionEmbedding: Vector @col(size:768): esse campo armazena as incorporações semânticas das descrições de filmes, permitindo a pesquisa de conteúdo baseada em vetor no seu app.

Ativar o Vertex AI

  1. Siga o guia de pré-requisitos para configurar as APIs da Vertex AI no Google Cloud. Essa etapa é essencial para oferecer suporte à geração de embedding e à funcionalidade de pesquisa de vetor.
  2. Reimplante seu esquema para ativar a pgvector e a pesquisa de vetores clicando em "Deploy to Production" usando a extensão do VS Code do Firebase Data Connect.

Preencher o banco de dados com embeddings

  1. Abra a pasta dataconnect no VS Code.
  2. Clique em Run(local) em optional_vector_embed.gql para preencher seu banco de dados com embeddings dos filmes.

b858da780f6ec103.png

Adicionar uma consulta de pesquisa de vetor

Em dataconnect/movie-connector/queries.gql, adicione a seguinte consulta para realizar pesquisas de vetores:

# Search movie descriptions using L2 similarity with Vertex AI
query SearchMovieDescriptionUsingL2Similarity($query: String!)
@auth(level: PUBLIC) {
  movies_descriptionEmbedding_similarity(
    compare_embed: { model: "textembedding-gecko@003", text: $query }
    method: L2
    within: 2
    limit: 5
  ) {
    id
    title
    description
    tags
    rating
    imageUrl
  }
}

Aprendizados importantes:

  • compare_embed: especifica o modelo de embedding (textembedding-gecko@003) e o texto de entrada ($query) para comparação.
  • method: especifica o método de similaridade (L2), que representa a distância euclidiana.
  • within: limita a pesquisa a filmes com uma distância L2 de 2 ou menos, com foco em correspondências de conteúdo próximas.
  • limit: restringe o número de resultados retornados a 5.

Implementar a função de pesquisa de vetor no app

Agora que o esquema e a consulta estão configurados, integre a pesquisa vetorial à camada de serviço do app. Essa etapa permite chamar a consulta de pesquisa do seu app da Web.

  1. Em app/src/lib/ MovieService.ts, desative o comentário das seguintes importações dos SDKs. Isso vai funcionar como qualquer outra consulta.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. Adicione a função a seguir para integrar a pesquisa baseada em vetores ao app:
    // Perform vector-based search for movies based on description
    export const searchMoviesByDescription = async (
      query: string
    ): Promise<
      | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"]
      | null
    > => {
      try {
        const response = await searchMovieDescriptionUsingL2similarity({ query });
        return response.data.movies_descriptionEmbedding_similarity;
      } catch (error) {
        console.error("Error fetching movie descriptions:", error);
        return null;
      }
    };
    

Aprendizados importantes:

  • searchMoviesByDescription: essa função chama a consulta searchMovieDescriptionUsingL2similarity, transmitindo o texto de entrada para realizar uma pesquisa de conteúdo baseada em vetor.

Veja isso na prática

Navegue até a seção "Pesquisa de vetor" na barra de navegação e digite frases como "romântico e moderno". Você vai encontrar uma lista de filmes que correspondem ao conteúdo que você está procurando. Também é possível acessar a página de detalhes de qualquer filme e conferir a seção de filmes semelhantes na parte de baixo da página.

7b71f1c75633c1be.png

12. Conclusão

Parabéns, você já pode usar o app da Web. Se você quiser usar seus próprios dados de filmes, não se preocupe. Insira seus dados usando a extensão do Firebase Data Connect, imitando os arquivos _insert.gql, ou adicione-os pelo painel de execução do Data Connect no VS Code.

Saiba mais