1. Antes de começar
Neste codelab, você vai integrar o Firebase Data Connect a um banco de dados do Cloud SQL para criar um app da Web de avaliação 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 estes recursos:
- Autenticação:implemente a autenticação personalizada para 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 web app de avaliação de filmes.
- Consultas e mutações SQL:recupere, atualize e gerencie dados no Cloud SQL usando consultas e mutações com tecnologia do 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 vetorial:adicione a funcionalidade de pesquisa de conteúdo usando a pesquisa vetorial do Firebase Data Connect para oferecer uma experiência do usuário avançada com base em entradas e preferências.
Pré-requisitos
É necessário ter um conhecimento básico 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.
- Escrever e testar várias consultas e mutações para um app de avaliação 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
- Git
- Visual Studio Code
- Instale o Node.js usando o nvm-windows (Windows) ou o nvm (macOS/Linux).
- Se você ainda não fez isso, crie um projeto do Firebase no Console do Firebase.
- (Opcional) Para a pesquisa vetorial, faça upgrade do seu projeto para o plano de preços Blaze de pagamento por uso.
2. Configurar o ambiente de desenvolvimento
Esta etapa do codelab vai orientar você na configuração do ambiente para começar a criar seu app de avaliação de filmes usando o Firebase Data Connect.
- 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
- Depois de executar esses comandos, abra http://localhost:5173 no navegador para ver o web app em execução local. Ele serve como front-end para criar o app de avaliação de filmes e interagir com os recursos dele.
- Abra a pasta
codelab-dataconnect-web
clonada usando o Visual Studio Code. É aqui que você vai definir o esquema, escrever consultas e testar a funcionalidade do app. - Para usar os recursos do Data Connect, instale a extensão do Firebase Data Connect para Visual Studio.
Também é possível instalar a extensão no Visual Studio Code Marketplace ou pesquisar por ela no VS Code. - Abra ou crie um projeto do Firebase no console do Firebase.
- Conecte seu projeto do Firebase à extensão do Firebase Data Connect no VSCode. Na extensão, faça o seguinte:
- Clique no botão Fazer login.
- Clique em Conectar um projeto do Firebase e selecione seu projeto.
- Inicie os emuladores do Firebase usando a extensão do Firebase Data Connect para VS Code:
clique em Iniciar emuladores e confirme se eles estão em execução no terminal.
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 oferecem uma visão geral da estrutura de pastas e arquivos do app.
O diretório dataconnect/
Contém configurações, conectores (que definem consultas e mutações) e arquivos de esquema do Firebase Data Connect.
schema/schema.gql
: define o esquema do GraphQL.connector/queries.gql
: consultas necessárias no seu appconnector/mutations.gql
: mutações necessárias no seu appconnector/connector.yaml
: arquivo de configuração para geração do 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 arquivoconnector/connector.yaml
, e os SDKs serão gerados automaticamente sempre que você definir uma consulta ou mutação.
4. Definir um esquema para avaliações 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 diretivas de esquema GraphQL. Depois de implementado, o app estará pronto para lidar com tudo, desde a pesquisa de filmes mais bem avaliados 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 por 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 interações do usuário, como avaliações e favoritos. Reviews
conectar usuários a filmes, permitindo que o app mostre classificações e feedback gerados pelos usuários.
As relações entre filmes, atores e usuários tornam o app mais dinâmico. A tabela de junção MovieActor
ajuda a mostrar detalhes do elenco e filmografias de atores. O tipo FavoriteMovie
permite que os usuários adicionem filmes aos favoritos. Assim, o app pode mostrar uma lista personalizada de favoritos e destacar as opções mais procuradas.
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]
}
Principais conclusões:
- 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 outros dados, 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
}
Principais conclusões:
- 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"
}
Principais conclusões:
- movie:faz referência ao tipo "Movie" e gera implicitamente uma chave estrangeira "movieId: UUID!".
- actor:faz referência ao tipo Actor e gera implicitamente uma chave externa actorId: UUID!.
- role:define a função do ator no filme (por exemplo, "principal" ou "secundário").
Configurar a tabela User
O tipo User
define uma entidade de usuário que interage com filmes deixando avaliações ou marcando filmes como favoritos.
Copie e cole o snippet de código no arquivo dataconnect/schema/schema.gql
:
type User
@table {
id: String! @col
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 junção que processa relações de muitos para muitos entre usuários e seus filmes favoritos. Cada tabela vincula um User
a um 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!
}
Principais conclusões:
- 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 um relacionamento 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")
}
Principais conclusões:
- user:faz referência ao usuário que deixou a avaliação.
- movie:faz referência ao filme que está sendo avaliado.
- reviewDate:definido automaticamente para o momento em que a avaliação é 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, o app de filmes tem uma base sólida para a estrutura e os relacionamentos de dados.
5. Recuperar os melhores e mais recentes filmes
Nesta seção, você vai inserir dados de filmes simulados nos emuladores locais e implementar os conectores (consultas) e o código TypeScript para chamar esses conectores no aplicativo da Web. No final, seu app poderá buscar e mostrar dinamicamente os filmes mais recentes e com melhor classificação diretamente do banco de dados.
Inserir dados simulados de filmes, atores e avaliações
- No VSCode, abra
dataconnect/moviedata_insert.gql
. Verifique se os emuladores na extensão do Firebase Data Connect estão em execução. - Você vai encontrar um botão Executar (local) na parte de cima do arquivo. Clique aqui para inserir os dados simulados de filmes no banco de dados.
- Verifique o terminal Execução do Data Connect para confirmar se os dados foram adicionados corretamente.
Implementar o conector
- Abra
dataconnect/movie-connector/queries.gql
. Você vai encontrar uma consulta básica doListMovies
nos comentários: Essa consulta busca todos os filmes e os detalhes deles (por exemplo,query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }
id
,title
,releaseYear
). No entanto, ela não classifica os filmes. - Substitua a consulta
ListMovies
atual pela seguinte 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 } }
- Clique no botão Executar (local) para executar a consulta no banco de dados local. Também é possível inserir as variáveis de consulta no painel de configuração antes de executar.
Principais conclusões:
movies()
:campo de consulta GraphQL para buscar dados de filmes do banco de dados.orderByRating
:parâmetro para classificar filmes por avaliação (crescente/decrescente).orderByReleaseYear
:parâmetro para classificar filmes por ano de lançamento (crescente/decrescente).limit
:restringe o número de filmes retornados.
Integrar consultas no web app
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 do arquivo connector.yaml
. Esses SDKs podem ser chamados diretamente no seu aplicativo.
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova o comentário da declaração de importação na parte de cima: A funçãoimport { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
, o tipo de respostaListMoviesData
e a enumeraçãoOrderDirection
são todos SDKs gerados pelos emuladores do Firebase Data Connect com base no esquema e nas consultas que você definiu anteriormente . - Substitua as funções
handleGetTopMovies
ehandleGetLatestMovies
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; } };
Principais conclusões:
listMovies
:uma função gerada automaticamente que chama a consultalistMovies
para recuperar uma lista de filmes. Ele 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 melhores e os filmes mais recentes na página inicial do app.
Veja isso na prática
Atualize o web app para ver a consulta em ação. A página inicial agora mostra dinamicamente a lista de filmes, buscando dados diretamente do banco de dados local. Os filmes mais recentes e com as melhores avaliações 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 recuperar informações detalhadas de um filme ou ator usando os IDs exclusivos deles. 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.
Implementar conectores
- Abra
dataconnect/movie-connector/queries.gql
no seu projeto. - Adicione as seguintes consultas para recuperar 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 } } }
- Salve as mudanças e revise as consultas.
Principais conclusões:
movie()
/actor()
: campos de consulta GraphQL para buscar um único filme ou ator na tabelaMovies
ouActors
._on_
: permite 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 de muitos para muitos por uma tabela de junção. Por exemplo,actors_via_MovieActor
acessa o tipoActor
pela tabela de junçãoMovieActor
, e a condiçãowhere
filtra os atores com base na função deles (por exemplo, "principal" ou "secundário").
Testar a consulta inserindo dados simulados
- No painel de execução do Data Connect, é possível testar a consulta inserindo IDs simulados, como:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- Clique em Executar (local) para
GetMovieById
e recupere os detalhes sobre "Paradoxo quântico", o filme simulado a que o ID acima se refere.
Integrar consultas no web app
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova o comentário das seguintes importações:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- Substitua as funções
handleGetMovieById
ehandleGetActorById
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; } };
Principais conclusões:
getMovieById
/getActorById
: são funções geradas automaticamente que chamam as consultas definidas, recuperando informações detalhadas sobre um filme ou ator específico.GetMovieByIdData
/GetActorByIdData
: são os tipos de resultados 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 ver todos os detalhes, incluindo atores e avaliações, informações extraídas de tabelas relacionadas. Da mesma forma, clicar em um ator vai mostrar 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 recuperar ou inserir dados do usuário diretamente no Firebase DataConnect, garantindo o gerenciamento seguro de usuários no seu app.
Implementar conectores
- Abra
mutations.gql
nodataconnect/movie-connector/
. - 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 } ) }
Principais conclusões:
id_expr: "auth.uid"
: usaauth.uid
, que é fornecido diretamente pelo Firebase Authentication, 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
- Abra
queries.gql
nodataconnect/movie-connector/
. - Adicione a seguinte consulta 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 } } } } }
Principais conclusões:
auth.uid
: recuperado diretamente do Firebase Authentication, garantindo acesso seguro a dados específicos do usuário.- Campos
_on_
: representam as tabelas de junção:reviews_on_user
: busca todas as avaliações relacionadas ao usuário, incluindo oid
e otitle
do filme.favorite_movies_on_user
: recupera todos os filmes marcados como favoritos pelo usuário, incluindo informações detalhadas comogenre
,releaseYear
,rating
emetadata
.
Integrar consultas no app da Web
- 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";
- Substitua as funções
handleAuthStateChange
ehandleGetCurrentUser
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; } };
Principais conclusões:
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çãoupsertUser
para criar ou atualizar as informações do usuário no banco de dados.handleGetCurrentUser
: busca o perfil do usuário atual usando a consultagetCurrentUser
, 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 tratamento de dados específicos do usuário no seu 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.
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
- Abra
mutations.gql
nodataconnect/movie-connector/
. - Adicione as seguintes mutações para processar a ação de adicionar filmes aos 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 }) }
Principais conclusões:
userId_expr: "auth.uid"
: usaauth.uid
, que é fornecido diretamente pelo Firebase Authentication, garantindo que apenas os dados do usuário autenticado sejam acessados ou modificados.
Verificar se um filme foi adicionado aos favoritos
- Abra
queries.gql
nodataconnect/movie-connector/
. - Adicione a consulta a seguir para verificar se um filme foi adicionado aos favoritos:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
Principais conclusões:
auth.uid
: garante o acesso seguro a dados específicos do usuário usando o Firebase Authentication.favorite_movie
: verifica a tabela de junçãofavorite_movies
para saber se um filme específico foi marcado como favorito pelo usuário atual.
Integrar consultas no web app
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova o comentário das seguintes importações:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Substitua as funções
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
ehandleGetIfFavoritedMovie
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; } };
Principais conclusões:
handleAddFavoritedMovie
ehandleDeleteFavoritedMovie
: use as mutações para adicionar ou remover um filme dos favoritos do usuário com segurança.handleGetIfFavoritedMovie
: usa a consultagetIfFavoritedMovie
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 perfil.
Permitir que os usuários deixem ou excluam avaliações
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 })
}
Principais conclusões:
userId_expr: "auth.uid"
: garante que as avaliações estejam 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 web app
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova o comentário das seguintes importações:import { addReview, deleteReview } from "@movie/dataconnect";
- Substitua as funções
handleAddReview
ehandleDeleteReview
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; } };
Principais conclusões:
handleAddReview
: chama a mutaçãoaddReview
para adicionar uma avaliação do filme especificado, vinculando-a com segurança ao usuário autenticado.handleDeleteReview
: usa a mutaçãodeleteReview
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 sobre filmes na página de detalhes. Eles também podem ver e excluir as avaliações na página do perfil, tendo controle total sobre as interações com o app.
9. Filtros avançados e correspondência parcial de texto
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é mesmo combinem vários filtros para resultados mais precisos.
Implementar conectores
- Abra
queries.gql
no appdataconnect/movie-connector/
. - Adicione a seguinte consulta 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 } } }
Principais conclusões:
- Operador
_and
: combina várias condições em uma única consulta, permitindo que a pesquisa seja filtrada por vários campos, comoreleaseYear
,rating
egenre
. - Operador
contains
: pesquisa correspondências parciais de texto em campos. Nessa consulta, ele procura correspondências emtitle
,description
,name
oureviewText
. - Cláusula
where
: especifica as condições para filtrar dados. Cada seção (filmes, atores, avaliações) usa uma cláusulawhere
para definir os critérios específicos da pesquisa.
Integrar consultas no web app
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova o comentário das seguintes importações:import { searchAll, SearchAllData } from "@movie/dataconnect";
- 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; } };
Principais conclusões:
handleSearchAll
: essa função usa a consultasearchAll
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 web app. Agora você pode pesquisar filmes, atores e avaliações usando vários filtros e entradas, recebendo resultados de pesquisa detalhados e personalizados.
10. Opcional: implante no Cloud (é necessário ter faturamento)
Agora que você concluiu a iteração de desenvolvimento local, é hora de implantar seu esquema, dados e consultas no servidor. Isso pode ser feito usando a extensão do Firebase Data Connect para 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 Blaze de pagamento por uso, 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:
- No console do Firebase, selecione Fazer upgrade do seu plano.
- 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.
Conecte seu app da Web ao projeto do Firebase
- Registre seu app da Web no projeto do Firebase usando o console do Firebase:
- Abra seu projeto e clique em Adicionar app.
- Ignore a configuração do SDK por enquanto, mas copie o objeto
firebaseConfig
gerado.
- Substitua o
firebaseConfig
atual emapp/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" };
- Crie o web app:de volta ao VS Code, na pasta
app
, use o Vite para criar o web app para implantação de hospedagem:cd app npm run build
Configurar o Firebase Authentication no seu projeto do Firebase
- Configure o Firebase Authentication com o Login do Google.
- (Opcional) Permita domínios para o Firebase Authentication usando o console do Firebase (por exemplo,
http://127.0.0.1
).- Nas configurações de Autenticação, acesse Domínios autorizados.
- Clique em "Adicionar domínio" e inclua seu domínio local na lista.
Implantar com a CLI do Firebase
- 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"]
- Verifique se a CLI do Firebase está configurada com seu projeto:
npm i -g firebase-tools firebase login --reauth firebase use --add
- No terminal, execute o seguinte comando para implantar:
firebase deploy --only dataconnect,hosting
- Execute este comando para comparar as mudanças no esquema:
firebase dataconnect:sql:diff
- 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 finais implantados. É possível monitorar o status no console do Firebase.
Agora você poderá ver o app em your-project.web.app/
. Além disso, clique em Executar (produção) 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 (é necessário faturamento)
Nesta seção, você vai ativar a pesquisa vetorial no app de avaliação 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.
Esta etapa exige que você tenha concluído a última etapa deste codelab para fazer a implantação no Google Cloud.
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
}
Principais conclusões:
descriptionEmbedding: Vector @col(size:768)
: esse campo armazena os embeddings semânticos das descrições de filmes, permitindo a pesquisa de conteúdo baseada em vetores no seu app.
Ativar a Vertex AI
- 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 embeddings e à funcionalidade de pesquisa vetorial.
- Implante novamente seu esquema para ativar a
pgvector
e a pesquisa de vetores clicando em "Implantar na produção" usando a extensão do Firebase Data Connect VS Code.
Preencher o banco de dados com embeddings
- Abra a pasta
dataconnect
no VS Code. - Clique em Executar(local) em
optional_vector_embed.gql
para preencher seu banco de dados com incorporações dos filmes.
Adicionar uma consulta de pesquisa vetorial
Em dataconnect/movie-connector/queries.gql
, adicione a seguinte consulta para fazer 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
}
}
Principais conclusões:
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, focando em correspondências de conteúdo próximas.limit
: restringe o número de resultados retornados a 5.
Implementar a função de pesquisa vetorial no app
Agora que o esquema e a consulta estão configurados, integre a pesquisa vetorial à camada de serviço do seu app. Essa etapa permite chamar a consulta de pesquisa do seu web app.
- Em
app/src/lib/
MovieService.ts
, remova o comentário das seguintes importações dos SDKs. Isso vai funcionar como qualquer outra consulta.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- Adicione a seguinte função 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; } };
Principais conclusões:
searchMoviesByDescription
: essa função chama a consultasearchMovieDescriptionUsingL2similarity
, 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 vetorial" na barra de navegação e digite frases como "romântico e moderno". Uma lista de filmes que correspondem ao conteúdo que você está procurando vai aparecer. Ou acesse a página de detalhes de qualquer filme e confira a seção "Filmes semelhantes" na parte de baixo da página.
12. Conclusão
Parabéns! Agora você pode usar o web app. Se 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.