1. Antes de comenzar
En este codelab, integrarás Firebase Data Connect con una base de datos de Cloud SQL para compilar una app web de opiniones sobre películas. La app completa muestra cómo Firebase Data Connect simplifica el proceso de compilación de aplicaciones potenciadas por SQL. Incluye las siguientes funciones:
- Autenticación: Implementa la autenticación personalizada para las consultas y mutaciones de tu app, lo que garantiza que solo los usuarios autorizados puedan interactuar con tus datos.
- Esquema de GraphQL: Crea y administra tus estructuras de datos con un esquema de GraphQL flexible adaptado a las necesidades de una app web de opiniones de películas.
- Consultas y mutaciones de SQL: Recupera, actualiza y administra datos en Cloud SQL con consultas y mutaciones potenciadas por GraphQL.
- Búsqueda avanzada con concordancia de cadena parcial: Usa filtros y opciones de búsqueda para encontrar películas en función de campos como el título, la descripción o las etiquetas.
- (Opcional) Integración de la búsqueda de vectores: Agrega la funcionalidad de búsqueda de contenido con la búsqueda de vectores de Firebase Data Connect para proporcionar una experiencia del usuario enriquecida en función de las entradas y preferencias.
Requisitos previos
Necesitarás conocimientos básicos de JavaScript.
Qué aprenderás
- Configura Firebase Data Connect con emuladores locales.
- Diseña un esquema de datos con Data Connect y GraphQL.
- Escribe y prueba varias consultas y mutaciones para una app de opiniones de películas.
- Obtén información sobre cómo Firebase Data Connect genera y usa el SDK en la app.
- Implementa tu esquema y administra la base de datos de manera eficiente.
Requisitos
- Git
- Visual Studio Code
- Instala Node.js con nvm-windows (Windows) o nvm (macOS/Linux).
- Si aún no lo hiciste, crea un proyecto de Firebase en Firebase console.
- (Opcional) Para la búsqueda vectorial, actualiza tu proyecto al plan de precios de pago por uso de Blaze
2. Configura tu entorno de desarrollo
En esta etapa del codelab, se te guiará para configurar el entorno y comenzar a compilar tu app de opiniones de películas con Firebase Data Connect.
- Clona el repositorio del proyecto y, luego, instala las dependencias necesarias:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- Después de ejecutar estos comandos, abre http://localhost:5173 en tu navegador para ver la app web ejecutándose de forma local. Esto funciona como frontend para compilar la app de opiniones de películas y para interactuar con sus funciones.
- Abre la carpeta
codelab-dataconnect-web
clonada con Visual Studio Code. Aquí es donde definirás tu esquema, escribirás consultas y probarás la funcionalidad de la app. - Para usar las funciones de Data Connect, instala la extensión de Visual Studio de Firebase Data Connect.
Como alternativa, puedes instalar la extensión desde Visual Studio Code Marketplace o buscarla en VS Code. - Abre o crea un proyecto de Firebase nuevo en Firebase console.
- Conecta tu proyecto de Firebase a la extensión de VSCode de Firebase Data Connect. En la extensión, haz lo siguiente:
- Haz clic en el botón Acceder.
- Haz clic en Conectar un proyecto de Firebase y selecciona tu proyecto de Firebase.
- Inicia los emuladores de Firebase con la extensión de VS Code de Firebase Data Connect:
Haz clic en Start Emulators y, luego, confirma que los emuladores se estén ejecutando en la terminal.
3. Revisa la base de código de partida
En esta sección, explorarás las áreas clave de la base de código de partida de la app. Si bien a la app le faltan algunas funciones, es útil comprender la estructura general.
Estructura de carpetas y archivos
En las siguientes sub secciones, se proporciona una descripción general de la estructura de archivos y carpetas de la app.
El directorio dataconnect/
Contiene configuraciones de Firebase Data Connect, conectores (que definen consultas y mutaciones) y archivos de esquemas.
schema/schema.gql
: Define el esquema de GraphQL.connector/queries.gql
: Consultas necesarias en tu appconnector/mutations.gql
: Mutaciones necesarias en tu appconnector/connector.yaml
: Es el archivo de configuración para la generación de SDK.
El directorio app/src/
Contiene la lógica de la aplicación y la interacción con Firebase Data Connect.
firebase.ts
: Es la configuración para conectarse a una app de Firebase en tu proyecto de Firebase.lib/dataconnect-sdk/
: Contiene el SDK generado. Puedes editar la ubicación de la generación de SDKs en el archivoconnector/connector.yaml
, y los SDKs se generarán automáticamente cada vez que definas una consulta o una mutación.
4. Define un esquema para las opiniones sobre películas
En esta sección, definirás la estructura y las relaciones entre las entidades clave de la aplicación de películas en un esquema. Las entidades como Movie
, User
, Actor
y Review
se asignan a tablas de bases de datos, con relaciones establecidas mediante Firebase Data Connect y directivas de esquema de GraphQL. Una vez que esté implementada, tu app estará lista para manejar todo, desde la búsqueda de películas con mejor calificación y el filtrado por género hasta permitir que los usuarios dejen opiniones, marquen favoritos, exploren películas similares o encuentren películas recomendadas en función de la entrada de texto a través de la búsqueda de vectores.
Entidades y relaciones principales
El tipo Movie
contiene detalles clave, como el título, el género y las etiquetas, que la app usa para las búsquedas y los perfiles de películas. El tipo User
realiza un seguimiento de las interacciones de los usuarios, como las opiniones y los favoritos. Reviews
conecta a los usuarios con películas, lo que permite que la app muestre calificaciones y comentarios generados por usuarios.
Las relaciones entre las películas, los actores y los usuarios hacen que la app sea más dinámica. La tabla de unión MovieActor
ayuda a mostrar los detalles del elenco y las filmografías de los actores. El tipo FavoriteMovie
permite a los usuarios marcar películas como favoritas para que la app pueda mostrar una lista de favoritos personalizada y destacar las selecciones populares.
Configura la tabla Movie
El tipo Movie
define la estructura principal de una entidad de película, incluidos campos como title
, genre
, releaseYear
y rating
.
Copia y pega el fragmento de código en tu archivo 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]
}
Conclusiones clave:
- id: Es un UUID único para cada película, generado con
@default(expr: "uuidV4()")
.
Configura la tabla MovieMetadata
El tipo MovieMetadata
establece una relación uno a uno con el tipo Movie
. Incluye datos adicionales, como el director de la película.
Copia y pega el fragmento de código en tu archivo 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
}
Conclusiones clave:
- Película @ref: Hace referencia al tipo
Movie
y establece una relación de clave externa.
Configura la tabla Actor
Copia y pega el fragmento de código en tu archivo dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
El tipo Actor
representa a un actor en la base de datos de películas, en la que cada actor puede formar parte de varias películas, formando una relación de varios a varios.
Configura la tabla MovieActor
Copia y pega el fragmento de código en tu archivo 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"
}
Conclusiones clave:
- movie: Hace referencia al tipo de película y genera implícitamente una clave externa movieId: UUID!.
- actor: Hace referencia al tipo de actor y genera implícitamente una clave externa actorId: UUID!.
- role: Define el rol del actor en la película (p.ej., "main" o "supporting").
Configura la tabla User
El tipo User
define una entidad de usuario que interactúa con las películas dejando opiniones o agregándolas a favoritos.
Copia y pega el fragmento de código en tu archivo 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
}
Configura la tabla FavoriteMovie
El tipo FavoriteMovie
es una tabla de unión que controla las relaciones varios a varios entre los usuarios y sus películas favoritas. Cada tabla vincula un User
a un Movie
.
Copia y pega el fragmento de código en tu archivo 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!
}
Conclusiones clave:
- movie: Hace referencia al tipo de película y genera implícitamente una clave externa
movieId: UUID!
. - user: Hace referencia al tipo de usuario y genera implícitamente una clave externa
userId: UUID!
.
Configura la tabla Review
El tipo Review
representa la entidad de opinión y vincula los tipos User
y Movie
en una relación varios a varios (un usuario puede dejar muchas opiniones y cada película puede tener muchas opiniones).
Copia y pega el fragmento de código en tu archivo 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")
}
Conclusiones clave:
- user: Hace referencia al usuario que dejó la opinión.
- movie: Hace referencia a la película que se está revisando.
- reviewDate: Se establece automáticamente en la hora en que se crea la opinión con
@default(expr: "request.time")
.
Campos generados automáticamente y valores predeterminados
El esquema usa expresiones como @default(expr: "uuidV4()")
para generar automáticamente IDs y marcas de tiempo únicos. Por ejemplo, el campo id
en los tipos Movie
y Review
se completa automáticamente con un UUID cuando se crea un registro nuevo.
Ahora que se definió el esquema, tu app de películas tiene una base sólida para su estructura de datos y relaciones.
5. Cómo recuperar las películas más populares y recientes
En esta sección, insertarás datos de películas simuladas en los emuladores locales y, luego, implementarás los conectores (consultas) y el código de TypeScript para llamar a estos conectores en la aplicación web. Al final, tu app podrá recuperar y mostrar de forma dinámica las películas con mejor calificación y las más recientes directamente desde la base de datos.
Inserta datos simulados de películas, actores y opiniones
- En VSCode, abre
dataconnect/moviedata_insert.gql
. Asegúrate de que los emuladores de la extensión de Firebase Data Connect estén en ejecución. - Deberías ver un botón Run (local) en la parte superior del archivo. Haz clic en este botón para insertar los datos de películas simuladas en tu base de datos.
- Verifica la terminal Data Connect Execution para confirmar que los datos se hayan agregado correctamente.
Implementa el conector
- Abre
dataconnect/movie-connector/queries.gql
. En los comentarios, encontrarás una consultaListMovies
básica: Esta consulta recupera todas las películas y sus detalles (por ejemplo,query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }
id
,title
yreleaseYear
). Sin embargo, no ordena las películas. - Reemplaza la consulta
ListMovies
existente por la siguiente para agregar opciones de ordenamiento y límite:# 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 } }
- Haz clic en el botón Run (local) para ejecutar la consulta en tu base de datos local. También puedes ingresar las variables de consulta en el panel de configuración antes de ejecutarlas.
Conclusiones clave:
movies()
: Es el campo de consulta de GraphQL para recuperar datos de películas de la base de datos.orderByRating
: Es un parámetro para ordenar las películas por clasificación (ascendente o descendente).orderByReleaseYear
: Es un parámetro para ordenar las películas por año de lanzamiento (ascendente o descendente).limit
: Restringe la cantidad de películas que se muestran.
Cómo integrar consultas en la app web
En esta parte del codelab, usarás las consultas definidas en la sección anterior en tu app web. Los emuladores de Firebase Data Connect generan SDKs en función de la información de los archivos .gql
(específicamente, schema.gql
, queries.gql
y mutations.gql
) y el archivo connector.yaml
. Se puede llamar directamente a estos SDKs en tu aplicación.
- En
MovieService
(app/src/lib/MovieService.tsx
), quita la marca de comentario de la sentencia de importación en la parte superior: La funciónimport { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
, el tipo de respuestaListMoviesData
y la enumeraciónOrderDirection
son todos SDKs que generan los emuladores de Firebase Data Connect según el esquema y las consultas que definiste anteriormente . - Reemplaza las funciones
handleGetTopMovies
yhandleGetLatestMovies
por el siguiente 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; } };
Conclusiones clave:
listMovies
: Es una función generada automáticamente que llama a la consultalistMovies
para recuperar una lista de películas. Incluye opciones para ordenar por clasificación o año de lanzamiento y limitar la cantidad de resultados.ListMoviesData
: Es el tipo de resultado que se usa para mostrar las 10 películas principales y las más recientes en la página principal de la app.
Vea cómo funciona
Vuelve a cargar tu app web para ver la consulta en acción. La página principal ahora muestra de forma dinámica la lista de películas y recupera datos directamente de tu base de datos local. Verás que las películas con mejor calificación y las más recientes aparecen sin problemas, lo que refleja los datos que acabas de configurar.
6. Cómo mostrar detalles de películas y actores
En esta sección, implementarás la funcionalidad para recuperar información detallada de una película o un actor con sus IDs únicos. Esto implica no solo recuperar datos de sus respectivas tablas, sino también unir tablas relacionadas para mostrar detalles completos, como críticas de películas y filmografías de actores.
Implementa conectores
- Abre
dataconnect/movie-connector/queries.gql
en tu proyecto. - Agrega las siguientes consultas para recuperar los detalles de las películas y los actores:
# 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 } } }
- Guarda los cambios y revisa las consultas.
Conclusiones clave:
movie()
/actor()
: Son campos de consulta de GraphQL para recuperar una sola película o actor de la tablaMovies
oActors
._on_
: Esto permite el acceso directo a los campos de un tipo asociado que tiene una relación de clave externa. Por ejemplo,reviews_on_movie
recupera todas las opiniones relacionadas con una película específica._via_
: Se usa para navegar por relaciones de muchos a muchos a través de una tabla de unión. Por ejemplo,actors_via_MovieActor
accede al tipoActor
a través de la tabla de uniónMovieActor
, y la condiciónwhere
filtra a los actores según su rol (por ejemplo, "principal" o "secundario").
Ingresa datos simulados para probar la consulta
- En el panel de ejecución de Data Connect, puedes probar la consulta ingresando IDs simulados, como los siguientes:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- Haz clic en Run (local) para que
GetMovieById
recupere los detalles sobre "Quantum Paradox" (la película simulada con la que se relaciona el ID anterior).
Cómo integrar consultas en la app web
- En
MovieService
(app/src/lib/MovieService.tsx
), quita el comentario de las siguientes importaciones:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- Reemplaza las funciones
handleGetMovieById
yhandleGetActorById
por el siguiente 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; } };
Conclusiones clave:
getMovieById
/getActorById
: Son funciones generadas automáticamente que llaman a las consultas que definiste y recuperan información detallada de una película o un actor específicos.GetMovieByIdData
/GetActorByIdData
: Estos son los tipos de resultados que se usan para mostrar los detalles de las películas y los actores en la app.
Vea cómo funciona
Ahora, ve a la página principal de tu app web. Haz clic en una película para ver todos sus detalles, incluidos los actores y las opiniones, que son datos extraídos de las tablas relacionadas. Del mismo modo, si haces clic en un actor, se mostrarán las películas en las que participó.
7. Controla la autenticación del usuario
En esta sección, implementarás la funcionalidad de acceso y cierre de sesión de los usuarios con Firebase Authentication. También usarás los datos de Firebase Authentication para recuperar o insertar datos de usuarios directamente en Firebase DataConnect, lo que garantiza una administración de usuarios segura en tu app.
Implementa conectores
- Abre
mutations.gql
endataconnect/movie-connector/
. - Agrega la siguiente mutación para crear o actualizar el usuario autenticado actual:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
Conclusiones clave:
id_expr: "auth.uid"
: Usaauth.uid
, que proporciona directamente Firebase Authentication, no el usuario ni la app, lo que agrega una capa adicional de seguridad, ya que garantiza que el ID de usuario se maneje de forma segura y automática.
Cómo recuperar el usuario actual
- Abre
queries.gql
endataconnect/movie-connector/
. - Agrega la siguiente consulta para recuperar el usuario actual:
# 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 } } } } }
Conclusiones clave:
auth.uid
: Se recupera directamente desde Firebase Authentication, lo que garantiza un acceso seguro a los datos específicos del usuario.- Campos
_on_
: Estos campos representan las tablas de unión:reviews_on_user
: Recupera todas las opiniones relacionadas con el usuario, incluidos losid
ytitle
de la película.favorite_movies_on_user
: Recupera todas las películas que el usuario marcó como favoritas, incluida información detallada comogenre
,releaseYear
,rating
ymetadata
.
Cómo integrar consultas en la app web
- En
MovieService
(app/src/lib/MovieService.tsx
), quita el comentario de las siguientes importaciones:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- Reemplaza las funciones
handleAuthStateChange
yhandleGetCurrentUser
por el siguiente 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; } };
Conclusiones clave:
handleAuthStateChange
: Esta función detecta cambios en el estado de autenticación. Cuando un usuario accede, se establecen sus datos y se llama a la mutaciónupsertUser
para crear o actualizar su información en la base de datos.handleGetCurrentUser
: Recupera el perfil del usuario actual con la consultagetCurrentUser
, que recupera las opiniones y las películas favoritas del usuario.
Vea cómo funciona
Ahora, haz clic en el botón "Acceder con Google" en la barra de navegación. Puedes acceder con el emulador de Firebase Authentication. Después de acceder, haz clic en “Mi perfil”. Por ahora, estará vacía, pero ya estableciste las bases para el manejo de datos específicos del usuario en tu app.
8. Cómo implementar interacciones del usuario
En esta sección del codelab, implementarás interacciones del usuario en la app de opiniones de películas, lo que les permitirá a los usuarios administrar sus películas favoritas y dejar o borrar opiniones.
Cómo permitir que un usuario agregue una película a favoritos
En esta sección, configurarás la base de datos para permitir que los usuarios agreguen una película a favoritos.
Implementa conectores
- Abre
mutations.gql
endataconnect/movie-connector/
. - Agrega las siguientes mutaciones para controlar las películas favoritas:
# 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 }) }
Conclusiones clave:
userId_expr: "auth.uid"
: Usaauth.uid
, que proporciona directamente Firebase Authentication, lo que garantiza que solo se acceda a los datos del usuario autenticado o se modifiquen.
Cómo verificar si una película está agregada a favoritos
- Abre
queries.gql
endataconnect/movie-connector/
. - Agrega la siguiente consulta para verificar si una película está agregada a favoritos:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
Conclusiones clave:
auth.uid
: Garantiza un acceso seguro a los datos específicos del usuario con Firebase Authentication.favorite_movie
: Verifica la tabla de uniónfavorite_movies
para ver si el usuario actual marcó una película específica como favorita.
Cómo integrar consultas en la app web
- En
MovieService
(app/src/lib/MovieService.tsx
), quita el comentario de las siguientes importaciones:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Reemplaza las funciones
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
yhandleGetIfFavoritedMovie
por el siguiente 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; } };
Conclusiones clave:
handleAddFavoritedMovie
yhandleDeleteFavoritedMovie
: Usa las mutaciones para agregar o quitar una película de los favoritos del usuario de forma segura.handleGetIfFavoritedMovie
: Usa la consultagetIfFavoritedMovie
para verificar si el usuario marcó una película como favorita.
Vea cómo funciona
Ahora, puedes marcar películas como favoritas o dejar de hacerlo haciendo clic en el ícono de corazón en las tarjetas de películas y en la página de detalles de la película. Además, puedes ver tus películas favoritas en la página de tu perfil.
Permite que los usuarios dejen o borren opiniones
A continuación, implementarás la sección para administrar las opiniones de los usuarios en la app.
Implementa conectores
En mutations.gql
(dataconnect/movie-connector/mutations.gql
), agrega las siguientes mutaciones:
# 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 })
}
Conclusiones clave:
userId_expr: "auth.uid"
: Garantiza que las opiniones estén asociadas con el usuario autenticado.reviewDate_date: { today: true }
: Genera automáticamente la fecha actual de la revisión con DataConnect, lo que elimina la necesidad de ingresar datos de forma manual.
Cómo integrar consultas en la app web
- En
MovieService
(app/src/lib/MovieService.tsx
), quita el comentario de las siguientes importaciones:import { addReview, deleteReview } from "@movie/dataconnect";
- Reemplaza las funciones
handleAddReview
yhandleDeleteReview
por el siguiente 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; } };
Conclusiones clave:
handleAddReview
: Llama a la mutaciónaddReview
para agregar una opinión a la película especificada y vincularla de forma segura al usuario autenticado.handleDeleteReview
: Usa la mutacióndeleteReview
para quitar una opinión de una película del usuario autenticado.
Vea cómo funciona
Ahora los usuarios pueden dejar opiniones sobre las películas en la página de detalles. También pueden ver y borrar sus opiniones en la página de su perfil, lo que les brinda control total sobre sus interacciones con la app.
9. Filtros avanzados y concordancia de texto parcial
En esta sección, implementarás funciones de búsqueda avanzada que permitirán a los usuarios buscar películas en función de una variedad de clasificaciones y años de lanzamiento, filtrar por géneros y etiquetas, realizar coincidencias de texto parciales en títulos o descripciones, y hasta combinar varios filtros para obtener resultados más precisos.
Implementa conectores
- Abre
queries.gql
endataconnect/movie-connector/
. - Agrega la siguiente consulta para admitir varias funciones de búsqueda:
# 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 } } }
Conclusiones clave:
- Operador
_and
: Combina varias condiciones en una sola consulta, lo que permite filtrar la búsqueda por varios campos, comoreleaseYear
,rating
ygenre
. - Operador
contains
: Busca coincidencias de texto parciales dentro de los campos. En esta consulta, busca coincidencias entitle
,description
,name
oreviewText
. - Cláusula
where
: Especifica las condiciones para filtrar los datos. Cada sección (películas, actores, opiniones) usa una cláusulawhere
para definir los criterios específicos de la búsqueda.
Cómo integrar consultas en la app web
- En
MovieService
(app/src/lib/MovieService.tsx
), quita el comentario de las siguientes importaciones:import { searchAll, SearchAllData } from "@movie/dataconnect";
- Reemplaza la función
handleSearchAll
por el siguiente 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; } };
Conclusiones clave:
handleSearchAll
: Esta función usa la consultasearchAll
para realizar una búsqueda basada en la entrada del usuario y filtrar los resultados por parámetros como el año, la calificación, el género y las coincidencias de texto parciales.
Vea cómo funciona
Ve a la página "Búsqueda avanzada" desde la barra de navegación de la app web. Ahora puedes buscar películas, actores y opiniones con varios filtros y entradas para obtener resultados de la búsqueda detallados y personalizados.
10. Opcional: Implementa en Cloud (se requiere facturación)
Ahora que completaste la iteración de desarrollo local, es hora de implementar tu esquema, tus datos y tus consultas en el servidor. Esto se puede hacer con la extensión de VS Code de Firebase Data Connect o Firebase CLI.
Actualiza tu plan de precios de Firebase
Para integrar Firebase Data Connect con Cloud SQL para PostgreSQL, tu proyecto de Firebase debe tener el plan de precios de pago por uso (Blaze), lo que significa que está vinculado a una cuenta de Facturación de Cloud.
- Una cuenta de Facturación de Cloud requiere una forma de pago, como una tarjeta de crédito.
- Si es la primera vez que usas Firebase y Google Cloud, verifica si cumples con los requisitos para obtener un crédito de USD 300 y una cuenta de Facturación de Cloud de prueba gratuita.
- Si realizas este codelab como parte de un evento, pregúntale al organizador si hay créditos de Cloud disponibles.
Para actualizar tu proyecto al plan Blaze, sigue estos pasos:
- En Firebase console, selecciona la opción para actualizar tu plan.
- Selecciona el plan Blaze. Sigue las instrucciones en pantalla para vincular una cuenta de Facturación de Cloud a tu proyecto.
Si necesitas crear una cuenta de Facturación de Cloud como parte de esta actualización, es posible que debas volver al flujo de actualización en Firebase console para completarla.
Cómo conectar tu app web a tu proyecto de Firebase
- Registra tu app web en tu proyecto de Firebase con Firebase console:
- Abre tu proyecto y, luego, haz clic en Agregar app.
- Por ahora, ignora la configuración del SDK, pero asegúrate de copiar el objeto
firebaseConfig
generado.
- Reemplaza el
firebaseConfig
existente enapp/src/lib/firebase.tsx
por la configuración que acabas de copiar de Firebase console.const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Compila la app web: En VS Code, en la carpeta
app
, usa Vite para compilar la app web para la implementación de hosting:cd app npm run build
Configura Firebase Authentication en tu proyecto de Firebase
- Configura Firebase Authentication con Acceso con Google.
- (Opcional) Permite dominios para Firebase Authentication con Firebase console (por ejemplo,
http://127.0.0.1
).- En la configuración de Authentication, ve a Authorized Domains.
- Haz clic en "Agregar dominio" y, luego, incluye tu dominio local en la lista.
Implementa con Firebase CLI
- En
dataconnect/dataconnect.yaml
, asegúrate de que el ID de la instancia, la base de datos y el ID del servicio coincidan con tu proyecto: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"]
- Asegúrate de tener Firebase CLI configurado con tu proyecto:
npm i -g firebase-tools firebase login --reauth firebase use --add
- En la terminal, ejecuta el siguiente comando para realizar la implementación:
firebase deploy --only dataconnect,hosting
- Ejecuta este comando para comparar los cambios de tu esquema:
firebase dataconnect:sql:diff
- Si los cambios son aceptables, aplícalos con lo siguiente:
firebase dataconnect:sql:migrate
Tu instancia de Cloud SQL para PostgreSQL se actualizará con el esquema y los datos finales que se hayan implementado. Puedes supervisar el estado en Firebase console.
Ahora deberías poder ver tu app en vivo en your-project.web.app/
. Además, puedes hacer clic en Run (Production) en el panel de Firebase Data Connect, tal como lo hiciste con los emuladores locales, para agregar datos al entorno de producción.
11. Opcional: Búsqueda de vectores con Firebase Data Connect (facturación obligatoria)
En esta sección, habilitarás la búsqueda vectorial en tu app de opiniones de películas con Firebase Data Connect. Esta función permite realizar búsquedas basadas en el contenido, como encontrar películas con descripciones similares mediante incorporaciones de vectores.
Para completar este paso, debes haber completado el último paso de este codelab para realizar la implementación en Google Cloud.
Actualiza el esquema para incluir incorporaciones para un campo
En dataconnect/schema/schema.gql
, agrega el campo descriptionEmbedding
a la tabla 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
}
Conclusiones clave:
descriptionEmbedding: Vector @col(size:768)
: Este campo almacena las incorporaciones semánticas de las descripciones de películas, lo que permite la búsqueda de contenido basado en vectores en tu app.
Cómo activar Vertex AI
- Sigue la guía de requisitos previos para configurar las APIs de Vertex AI desde Google Cloud. Este paso es esencial para admitir la generación de incorporaciones y la funcionalidad de búsqueda vectorial.
- Vuelve a implementar tu esquema para activar
pgvector
y la búsqueda de vectores. Para ello, haz clic en "Deploy to Production" con la extensión de VS Code de Firebase Data Connect.
Completa la base de datos con incorporaciones
- Abre la carpeta
dataconnect
en VS Code. - Haz clic en Run(local) en
optional_vector_embed.gql
para propagar tu base de datos con incorporaciones para las películas.
Cómo agregar una consulta de Búsqueda de vectores
En dataconnect/movie-connector/queries.gql
, agrega la siguiente consulta para realizar búsquedas vectoriales:
# 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
}
}
Conclusiones clave:
compare_embed
: Especifica el modelo de incorporación (textembedding-gecko@003
) y el texto de entrada ($query
) para la comparación.method
: Especifica el método de similitud (L2
), que representa la distancia euclidiana.within
: Limita la búsqueda a películas con una distancia de L2 de 2 o menos, y se enfoca en las coincidencias de contenido cercanas.limit
: Restringe la cantidad de resultados que se muestran a 5.
Implementa la función de búsqueda vectorial en tu app
Ahora que el esquema y la consulta están configurados, integra la búsqueda vectorial en la capa de servicio de tu app. Este paso te permite llamar a la búsqueda desde tu app web.
- En
app/src/lib/
MovieService.ts
, quita la marca de comentario de las siguientes importaciones de los SDKs. Esto funcionará como cualquier otra consulta.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- Agrega la siguiente función para integrar la búsqueda basada en vectores en la 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; } };
Conclusiones clave:
searchMoviesByDescription
: Esta función llama a la consultasearchMovieDescriptionUsingL2similarity
y pasa el texto de entrada para realizar una búsqueda de contenido basada en vectores.
Vea cómo funciona
Navega a la sección "Búsqueda vectorial" en la barra de navegación y escribe frases como "romántica y moderna". Verás una lista de películas que coinciden con el contenido que buscas. También puedes ir a la página de detalles de cualquier película y consultar la sección de películas similares en la parte inferior de la página.
12. Conclusión
¡Felicitaciones! Deberías poder usar la app web. Si quieres jugar con tus propios datos de películas, no te preocupes, inserta tus propios datos con la extensión de Firebase Data Connect imitando los archivos _insert.gql
o agrégalos a través del panel de ejecución de Data Connect en VS Code.