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 análise de filmes. O aplicativo 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: recupere, atualize e gerencie dados no Cloud SQL usando consultas e mutações com a 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 de vetores: adicione a funcionalidade de pesquisa de conteúdo usando a pesquisa de vetores do Firebase Data Connect para oferecer uma experiência de usuário rica com base nas entradas e preferências.
Pré-requisitos
É preciso ter um conhecimento básico de JavaScript.
O que você aprenderá
- Configurar 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 você vai precisar
- Git
- Visual Studio Code
- Instale o Node.js usando o nvm-windows (Windows) ou o nvm (macOS/Linux).
- Crie um projeto do Firebase no Console do Firebase, caso ainda não tenha feito isso.
- (Opcional) Para a pesquisa de vetores, faça upgrade do seu projeto para o plano Blaze
Como configurar o ambiente para desenvolvedores
Esta seção vai orientar você na configuração do ambiente para começar a criar seu app de análise de filmes usando o Firebase Data Connect.
Etapa 1: clonar o repositório do projeto
Comece clonando o repositório do projeto e instalando 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 os comandos, abra http://localhost:5173 no seu navegador para ver o app da Web sendo executado localmente. Ele serve como front-end para criar o app de crítica de filmes e interagir com os recursos dele.
Etapa 2: abrir o projeto no Visual Studio Code
Abra a pasta clonada codelab-dataconnect-web
usando o Visual Studio Code. É aqui que você define seu esquema, escreve consultas e testa a funcionalidade do app.
Etapa 3: instalar a extensão do Visual Studio do Firebase Data Connect
Para usar os recursos do Data Connect, instale a extensão do Visual Studio do Firebase Data Connect.Você também pode fazer isso no Marketplace do Visual Studio Code ou pesquisar no VS Code.
- Ou instalar pelo Visual Studio Code Marketplace ou pesquisar no VS Code.
Etapa 4: criar um projeto do Firebase
Acesse o Console do Firebase para criar um novo projeto do Firebase, caso ainda não tenha um. Em seguida, na extensão do VSCode do Firebase Data Connect:
- Clique no botão Fazer login.
- Clique em Conectar um projeto do Firebase e selecione o projeto que você criou no console do Firebase.
Etapa 5: iniciar emuladores do Firebase
Na extensão do VSCode do Firebase Data Connect, clique em "Start Emulators" e confirme se os emuladores estão em execução no terminal.
2. 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
Confira uma visão geral rápida da estrutura de pastas e arquivos do app:
conexão de dados/
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 GraphQLconnector/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 do SDK
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 console.lib/dataconnect-sdk/
: essa pasta 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.
3. Como definir um esquema para a análise de filmes
Nesta seção, você vai definir a estrutura e as relações entre as principais entidades no aplicativo de filme em um esquema. Entidades como Movie
, User
, Actor
e Review
são mapeadas para tabelas de banco de dados, com relacionamentos estabelecidos usando diretivas de esquema do Firebase Data Connect e GraphQL. Quando estiver pronto, seu app estará pronto para lidar com tudo, desde pesquisar filmes com boas avaliações e filtrar por gênero até permitir que os usuários façam avaliações, marquem favoritos, explorem filmes semelhantes ou encontrem filmes recomendados com base em entrada de texto por meio da pesquisa vetorial.
Entidades e relações 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.
Tabela de filmes
O tipo "Filme" define a estrutura principal de uma entidade de filme, incluindo campos como título, gênero, ano de lançamento e classificação.
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()")
.
Tabela MovieMetadata
O tipo MovieMetadata estabelece uma relação 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.
Tabela de atores
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.
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", 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 "apoio").
Tabela de usuários
O tipo User
define uma entidade de usuário que interage com os 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
}
Tabela FavoriteMovie
O tipo FavoriteMovie
é uma tabela de mesclagem que processa relacionamentos de muitos para muitos entre usuários e os filmes ou atores favoritos deles. 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!
}
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.
Tabela de revisão
O tipo de resenha representa a entidade da resenha e vincula os tipos Usuário e Filme em um relacionamento de muitos para muitos (um usuário pode deixar muitas resenhas, e cada filme pode ter muitas críticas).
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 avaliado.
- reviewDate: definida automaticamente como o horário 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 "Filme" e "Avaliação" é 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.
4. Como recuperar filmes principais e mais recentes
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. Ao final, o aplicativo poderá buscar e exibir dinamicamente os filmes mais recentes e melhor avaliados diretamente do banco de dados.
Como inserir dados de filmes, atores e resenhas
- 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 Run (local) na parte de cima do arquivo. Clique aqui para inserir os dados fictícios do filme no banco de dados.
- Verifique o terminal Data Connect Execution para confirmar se os dados foram adicionados.
Como implementar o conector
- Abra
dataconnect/movie-connector/queries.gql
. Você vai encontrar uma consultaListMovies
básica nos comentários:
query ListMovies @auth(level: PUBLIC) {
movies {
id
title
imageUrl
releaseYear
genre
rating
tags
description
}
}
Esta consulta busca todos os filmes e seus detalhes (por exemplo, id, title, releaseYear). No entanto, ele não classifica os filmes.
- Substitua a consulta
ListMovies
pela consulta abaixo 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 seu banco de dados local. Também é possível inserir as variáveis de consulta no painel de configuração antes da execução.
Aprendizados importantes:
- movies(): campo de consulta do GraphQL para buscar dados de filmes do banco de dados.
- orderByRating: parâmetro para classificar filmes por classificação (em ordem crescente/decrescente).
- orderByReleaseYear: parâmetro para classificar filmes por ano de lançamento (ascendente/descendente).
- limit: restringe o número de filmes retornados.
Como integrar consultas no app da Web
Nesta parte, 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 nos arquivos .gql (schema.gql, query.gql, Muts.gql) e Connector.yaml. Esses SDKs podem ser chamados diretamente no aplicativo.
- 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.
- 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;
}
};
Aprendizados importantes:
- listMovies: é uma função gerada automaticamente que chama a consulta "listMovies" 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 exibir os 10 principais filmes e os mais recentes na página inicial.
Veja na prática
Atualize seu app da Web para ver a consulta em ação. A página inicial agora mostra dinamicamente a lista de filmes, buscando dados diretamente do seu banco de dados local. Você verá os filmes mais bem avaliados e os mais recentes aparecerem perfeitamente, refletindo os dados que você acabou de configurar.
5. Como 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 a busca de dados das respectivas tabelas, mas também a junção de tabelas relacionadas para exibir detalhes abrangentes, como críticas de filmes e filmografias de atores.
Como implementar conectores
- Abra
dataconnect/movie-connector/queries.gql
no seu projeto. - 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
}
}
}
- Salve as mudanças 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 tenha um relacionamento de chave externa. Por exemplo,reviews_on_movie
busca todas as avaliações relacionadas a um filme específico._via_
: usada para navegar em relacionamentos de muitos para muitos por meio de uma tabela de mesclagem. Por exemplo,actors_via_MovieActor
acessa o tipo "Ator" pela tabela de junção MovieActor, e a condiçãowhere
filtra atores com base no papel (por exemplo, "principal" ou "apoio").
No painel de execução do Data Connect, é possível testar a consulta inserindo IDs falsos, como:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
Clique em Run (local) em GetMovieById
para recuperar os detalhes sobre "Quantum Paradox" (Paradoxo quântico) (o filme simulado com o qual o ID acima se refere).
Como integrar consultas no app da Web
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova a marca de 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;
}
};
Aprendizados importantes:
getMovieById
/getActorById
: são funções geradas automaticamente que chamam as consultas que você definiu, recuperando informações detalhadas de um filme ou ator específico.GetMovieByIdData
/GetActorByIdData
: são os tipos de resultados usados para mostrar detalhes de filmes e atores no app.
Confira na prática
Agora, vá para 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.
6. Como processar a autenticação do usuário
Nesta seção, você vai implementar a funcionalidade de login e logout dos usuários com o Firebase Authentication. Você também usará os dados do Firebase Authentication para recuperar ou inserir e atualizar dados do usuário diretamente no Firebase DataConnect, garantindo o gerenciamento seguro de usuários no seu aplicativo.
Como 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
}
)
}
Aprendizado importante:
id_expr: "auth.uid"
: usa oauth.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 tratado de maneira segura e automática.
Em seguida, abra queries.gql
no dataconnect/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
}
}
}
}
}
Pontos-chave:
auth.uid
: é extraído diretamente do Firebase Authentication, garantindo o acesso seguro a dados específicos do usuário._on_
Campos: esses campos representam as tabelas mescladas:reviews_on_user
: busca todas as avaliações relacionadas ao usuário, incluindo o ID e o título do filme.favorite_movies_on_user
: recupera todos os filmes marcados como favoritos pelo usuário, incluindo informações detalhadas como gênero, lançamentoAno, classificação e metadados.
Como integrar consultas no app da Web
- No MovieService (
app/src/lib/MovieService.tsx
), remova a marca de 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;
}
};
Aprendizados importantes:
handleAuthStateChange
: esta função detecta alterações 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 do usuário e os filmes favoritos dele.
Veja 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 Auth. 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.
7. Como implementar interações do usuário
Nesta seção, você vai implementar interações do usuário no app de crítica de filmes. Assim, os usuários poderão gerenciar os filmes favoritos deles e deixar ou excluir avaliações.
Como implementar conectores
- Abra
mutations.gql
nodataconnect/movie-connector/
. - 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"
: usaauth.uid
, que é fornecido diretamente pelo Firebase Authentication, garantindo que apenas os dados do usuário autenticado sejam acessados ou modificados.
- Em seguida, abra
queries.gql
nodataconnect/movie-connector/
. - Adicione a consulta abaixo para verificar se um filme é 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 mesclagemfavorite_movies
para ver se um filme específico está marcado como favorito pelo usuário atual.
Como integrar consultas no app da Web
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova a marca de 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;
}
};
Aprendizados importantes:
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 na prática
Agora, você pode marcar filmes como favoritos ou removê-los clicando no ícone de coração nos cards e na página de detalhes do filme. Além disso, você pode visualizar seus filmes favoritos na página de seu perfil.
Como implementar avaliações de usuários
Em seguida, você vai implementar a seção para gerenciar as avaliações dos usuários no app.
Como 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 revisão usando o DataConnect, eliminando a necessidade de entrada manual.
Como integrar consultas no app da Web
- Em
MovieService
(app/src/lib/MovieService.tsx
), remova a marca de 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;
}
};
Aprendizados importantes:
handleAddReview
: chama a mutaçãoaddReview
para adicionar uma avaliação do filme especificado, vinculando-o de maneira segura ao usuário autenticado.handleDeleteReview
: usa a mutaçãodeleteReview
para remover uma resenha de um filme feita pelo usuário autenticado.
Veja 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.
8. 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.
Como implementar conectores
- Abrir
queries.gql
no appdataconnect/movie-connector/
. - 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, comoreleaseYear
,rating
egenre
. - Operador
contains
: pesquisa correspondências de texto parcial nos 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, críticas) usa uma cláusulawhere
para definir os critérios específicos da pesquisa.
Como integrar consultas no app da Web
- Em
MovieService
(app/src/lib/MovieService.tsx
), descomente as seguintes importações:
import { searchAll, SearchAllData } from "@movie/dataconnect";
- Substitua a função
handleSearchAll
por este 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 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 na prática
Acesse a "Pesquisa avançada" página da barra de navegação no aplicativo da web. Agora você pode pesquisar filmes, atores e avaliações usando vários filtros e entradas para receber resultados detalhados e personalizados.
9. Opcional: implantar na nuvem (requer cobrança)
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.
Como adicionar um app da Web ao Console do Firebase
- Crie um app da Web no Console do Firebase e anote o ID do app.
- Configure um app da Web no console do Firebase clicando em "Adicionar app". Por enquanto, você pode ignorar a configuração do SDK e a configuração, mas anote o objeto
firebaseConfig
gerado. - Substitua o
firebaseConfig
emapp/src/lib/firebase.tsx
:
const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.appspot.com", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Crie o app da Web: na pasta
app
, use o Vite para criar o app da Web que hospedará a implantação:
cd app npm run build
Configurar o Firebase Authentication no console
- Configurar o Firebase Auth com o Login do Google
- (Opcional) Permita domínios para (Firebase Auth) [https://firebase.google.com/docs/auth/web/hosting] no console do projeto (por exemplo,
http://127.0.0.1
):
- Nas configurações de autenticação, selecione seu projeto e acesse (Domínios autorizados) [https://firebase.google.com/docs/auth/web/hosting]. 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 alterações 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ê pode acessar o app em your-project.web.app/
. Além disso, você pode clicar 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.
10. Opcional: pesquisa de vetores com o Firebase Data Connect
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.
Atualizar o esquema para incluir incorporações de um campo
- Em
dataconnect/schema/schema.gql
, adicione o campodescriptionEmbedding
à tabelaMovie
:
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
}
Aprendizado importante:
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.
Como ativar a Vertex AI
- Siga o guia de pré-requisitos para configurar as APIs Vertex AI com o Google Cloud. Essa etapa é essencial para oferecer suporte à geração de embedding e à funcionalidade de pesquisa vetorial.
- Reimplante seu esquema para ativar a pesquisa de
pgvector
e vetor clicando em "Implantar na produção" usando a extensão do VSCode do Firebase Data Connect.
Como preencher o banco de dados com embeddings
- Abra a pasta
dataconnect
no VSCode e clique em Run(local) emoptional_vector_embed.gql
para preencher o banco de dados com incorporações dos filmes.
Adicionar uma consulta de pesquisa de vetor
- Em
dataconnect/movie-connector/queries.gql
, adicione a seguinte consulta para realizar pesquisas vetoriais:
# 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
- Em
app/src/lib/MovieService.ts
, remova a marca de comentário das seguintes importações:
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 app. Essa etapa permite chamar a consulta de pesquisa do seu app da Web.
No app/src/lib/
MovieService.ts
, remova a marca de comentário das seguintes importações dos SDKs. Isso vai funcionar como qualquer outra consulta.
import {
searchMovieDescriptionUsingL2similarity,
SearchMovieDescriptionUsingL2similarityData,
} from "@movie/dataconnect";
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 consultasearchMovieDescriptionUsingL2similarity
, transmitindo o texto de entrada para realizar uma pesquisa de conteúdo baseada em vetores.
Veja na prática
Navegue até "Pesquisa Vetorial". 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 ou pode acessar a página de detalhes de qualquer filme e conferir a seção de filmes semelhantes na parte de baixo da página.
11. 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 FDC, imitando os arquivos _insert.gql ou adicionando-os pelo painel de execução do Data Connect.