Mit Firebase Data Connect entwickeln

1. Hinweis

FriendlyMovies App

In diesem Codelab integrieren Sie Firebase Data Connect in eine Cloud SQL-Datenbank, um eine Webanwendung für Filmbewertungen zu erstellen. Die fertige App zeigt, wie Firebase Data Connect das Erstellen von SQL-basierten Anwendungen vereinfacht. Sie bietet folgende Funktionen:

  • Authentifizierung:Implementieren Sie eine benutzerdefinierte Authentifizierung für die Abfragen und Mutationen Ihrer App, damit nur autorisierte Nutzer mit Ihren Daten interagieren können.
  • GraphQL-Schema: Erstellen und verwalten Sie Ihre Datenstrukturen mit einem flexiblen GraphQL-Schema, das auf die Anforderungen einer Web-App für Filmbewertungen zugeschnitten ist.
  • SQL-Abfragen und ‑Mutationen:Mithilfe von Abfragen und Mutationen, die auf GraphQL basieren, können Sie Daten in Cloud SQL abrufen, aktualisieren und verwalten.
  • Erweiterte Suche mit teilweiser Übereinstimmung:Mithilfe von Filtern und Suchoptionen können Sie Filme anhand von Feldern wie Titel, Beschreibung oder Tags finden.
  • Optional : Vektorsuche einbinden: Fügen Sie mit der Vektorsuche von Firebase Data Connect eine Inhaltssuchfunktion hinzu, um eine umfassende Nutzererfahrung basierend auf Eingaben und Einstellungen zu ermöglichen.

Voraussetzungen

Sie benötigen grundlegende Kenntnisse von JavaScript.

Aufgaben in diesem Lab

  • Richten Sie Firebase Data Connect mit lokalen Emulatoren ein.
  • Erstellen Sie ein Datenschema mit Data Connect und GraphQL.
  • Verschiedene Abfragen und Mutationen für eine Filmbewertungs-App schreiben und testen
  • Informationen dazu, wie Firebase Data Connect das SDK in der App generiert und verwendet
  • Schema bereitstellen und Datenbank effizient verwalten

Voraussetzungen

2. Entwicklungsumgebung einrichten

In dieser Phase des Codelabs erfahren Sie, wie Sie die Umgebung einrichten, um mit Firebase Data Connect Ihre Filmrezensions-App zu erstellen.

  1. Klonen Sie das Projekt-Repository und installieren Sie die erforderlichen Abhängigkeiten:
    git clone https://github.com/firebaseextended/codelab-dataconnect-web
    cd codelab-dataconnect-web
    cd ./app && npm i
    npm run dev
    
  2. Öffnen Sie nach Ausführung dieser Befehle http://localhost:5173 in Ihrem Browser, um die lokal ausgeführte Webanwendung zu sehen. Dies dient als Frontend für die Erstellung der Filmbewertungs-App und die Interaktion mit ihren Funktionen.93f6648a2532c606.png
  3. Öffnen Sie den geklonten Ordner codelab-dataconnect-web mit Visual Studio Code. Hier definieren Sie Ihr Schema, schreiben Abfragen und testen die Funktionen der App.
  4. Wenn Sie die Data Connect-Funktionen verwenden möchten, installieren Sie die Visual Studio-Erweiterung für Firebase Data Connect.
    Alternativ können Sie die Erweiterung auch über den Visual Studio Code Marketplace installieren oder in VS Code danach suchen.b03ee38c9a81b648.png
  5. Öffnen Sie ein Firebase-Projekt in der Firebase Console oder erstellen Sie ein neues.
  6. Verbinden Sie Ihr Firebase-Projekt mit der VSCode-Erweiterung „Firebase Data Connect“. Führen Sie in der Erweiterung die folgenden Schritte aus:
    1. Klicken Sie auf die Schaltfläche Anmelden.
    2. Klicken Sie auf Firebase-Projekt verknüpfen und wählen Sie Ihr Firebase-Projekt aus.
    4bb2fbf8f9fac29b.png
  7. Starten Sie die Firebase-Emulatoren mit der VS Code-Erweiterung „Firebase Data Connect“:
    Klicken Sie auf Emulatoren starten und prüfen Sie dann, ob die Emulatoren im Terminal ausgeführt werden.6d3d95f4cb708db1.png

3. Starter-Codebasis ansehen

In diesem Abschnitt lernen Sie die wichtigsten Bereiche der Start-Codebasis der App kennen. Auch wenn der App einige Funktionen fehlen, ist es hilfreich, die Gesamtstruktur zu verstehen.

Ordner- und Dateistruktur

Die folgenden Unterabschnitte geben einen Überblick über die Ordner- und Dateistruktur der App.

Verzeichnis dataconnect/

Enthält Firebase Data Connect-Konfigurationen, Connectors (mit denen Abfragen und Mutationen definiert werden) und Schemadateien.

  • schema/schema.gql: Definiert das GraphQL-Schema
  • connector/queries.gql: In Ihrer App erforderliche Abfragen
  • connector/mutations.gql: In Ihrer App erforderliche Mutationen
  • connector/connector.yaml: Konfigurationsdatei für die SDK-Generierung

Verzeichnis app/src/

Enthält die Anwendungslogik und die Interaktion mit Firebase Data Connect.

  • firebase.ts: Konfiguration für die Verbindung zu einer Firebase App in Ihrem Firebase-Projekt.
  • lib/dataconnect-sdk/: Enthält das generierte SDK. Sie können den Speicherort der SDK-Generierung in der connector/connector.yaml-Datei bearbeiten. SDKs werden dann automatisch generiert, sobald Sie eine Abfrage oder Mutation definieren.

4. Schema für Filmrezensionen definieren

In diesem Abschnitt definieren Sie die Struktur und die Beziehungen zwischen den wichtigsten Entitäten in der Filmanwendung in einem Schema. Entitäten wie Movie, User, Actor und Review werden Datenbanktabellen zugeordnet. Die Beziehungen werden mit Firebase Data Connect- und GraphQL-Schemarichtlinien festgelegt. Sobald die Funktion eingerichtet ist, kann Ihre App alles verarbeiten, von der Suche nach den bestbewerteten Filmen und dem Filtern nach Genre bis hin zur Möglichkeit für Nutzer, Rezensionen zu verfassen, Favoriten zu markieren, ähnliche Filme zu entdecken oder empfohlene Filme anhand von Texteingaben über die Vektorsuche zu finden.

Grundlegende Entitäten und Beziehungen

Der Typ Movie enthält wichtige Details wie Titel, Genre und Tags, die in der App für Suchanfragen und Filmprofile verwendet werden. Der Typ User erfasst Nutzerinteraktionen wie Rezensionen und Favoriten. Reviews Nutzer mit Filmen verbinden, sodass die App von Nutzern erstellte Bewertungen und Feedback anzeigt.

Die Beziehungen zwischen Filmen, Schauspielern und Nutzern machen die App dynamischer. Die MovieActor-Join-Tabelle hilft dabei, Details zur Besetzung und Filmografie der Schauspieler anzuzeigen. Mit dem Typ FavoriteMovie können Nutzer Filme zu ihren Favoriten hinzufügen. So kann die App eine personalisierte Favoritenliste anzeigen und beliebte Titel hervorheben.

Tabelle Movie einrichten

Der Typ Movie definiert die Hauptstruktur einer Filmentität, einschließlich Feldern wie title, genre, releaseYear und rating.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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

Zusammenfassung:

  • id:Eine eindeutige UUID für jeden Film, die mit @default(expr: "uuidV4()") generiert wird.

Tabelle MovieMetadata einrichten

Der Typ MovieMetadata stellt eine Eins-zu-eins-Beziehung mit dem Typ Movie her. Sie enthält zusätzliche Daten wie den Regisseur des Films.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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
}

Zusammenfassung:

  • Film! @ref: verweist auf den Typ Movie und stellt eine Fremdschlüsselbeziehung her.

Tabelle Actor einrichten

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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

Der Typ Actor steht für einen Schauspieler in der Filmdatenbank. Jeder Schauspieler kann an mehreren Filmen mitwirken, was eine Beziehung vom Typ „Viele zu Viele“ bildet.

Tabelle MovieActor einrichten

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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"
}

Zusammenfassung:

  • movie:Verweist auf den Filmtyp und generiert implizit einen Fremdschlüssel „movieId: UUID!“.
  • actor:Verweist auf den Actor-Typ und generiert implizit einen Fremdschlüssel „actorId: UUID!“.
  • role:Definiert die Rolle des Schauspielers im Film (z.B. „main“ oder „supporting“).

Tabelle User einrichten

Der Typ User definiert eine Nutzerentität, die mit Filmen interagiert, indem sie Rezensionen hinterlässt oder Filme zu ihren Favoriten hinzufügt.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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
}

Tabelle FavoriteMovie einrichten

Der Typ FavoriteMovie ist eine Join-Tabelle, die Mehrfachbeziehungen zwischen Nutzern und ihren Lieblingsfilmen verarbeitet. Jede Tabelle verknüpft eine User mit einer Movie.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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

Zusammenfassung:

  • movie:Verweist auf den Filmtyp und generiert implizit einen Fremdschlüssel movieId: UUID!.
  • user:Verweist auf den Nutzertyp und generiert implizit einen Fremdschlüssel userId: UUID!.

Tabelle Review einrichten

Der Typ Review steht für die Rezensions-Entität und verknüpft die Typen User und Movie in einer m:n-Beziehung. Ein Nutzer kann viele Rezensionen hinterlassen und jeder Film kann viele Rezensionen haben.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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

Zusammenfassung:

  • user:Verweis auf den Nutzer, der die Rezension abgegeben hat.
  • movie:Verweis auf den Film, der rezensiert wird.
  • reviewDate:Wird automatisch auf die Uhrzeit festgelegt, zu der die Rezension mit @default(expr: "request.time") erstellt wurde.

Automatisch generierte Felder und Standardwerte

Im Schema werden Ausdrücke wie @default(expr: "uuidV4()") verwendet, um automatisch eindeutige IDs und Zeitstempel zu generieren. Beispielsweise wird das Feld id bei den Typen Movie und Review beim Erstellen eines neuen Eintrags automatisch mit einer UUID ausgefüllt.

Nachdem das Schema definiert wurde, hat Ihre Film-App eine solide Grundlage für die Datenstruktur und die Beziehungen.

5. Top- und aktuelle Filme abrufen

FriendlyMovies App

In diesem Abschnitt fügen Sie den lokalen Emulatoren Mock-Filmdaten hinzu und implementieren dann die Connectors (Abfragen) und den TypeScript-Code, um diese Connectors in der Webanwendung aufzurufen. Am Ende kann Ihre App die am besten bewerteten und neuesten Filme dynamisch direkt aus der Datenbank abrufen und anzeigen.

Mockup-Film-, Schauspieler- und Rezensionsdaten einfügen

  1. Öffnen Sie in VS Code dataconnect/moviedata_insert.gql . Prüfen Sie, ob die Emulatoren in der Firebase Data Connect-Erweiterung ausgeführt werden.
  2. Oben in der Datei sollte die Schaltfläche Ausführen (lokal) angezeigt werden. Klicken Sie darauf, um die simulierten Filmdaten in Ihre Datenbank einzufügen.
    e424f75e63bf2e10.png
  3. Prüfen Sie im Terminal Data Connect Execution, ob die Daten erfolgreich hinzugefügt wurden.
    e0943d7704fb84ea.png

Connector implementieren

  1. Öffnen Sie dataconnect/movie-connector/queries.gql. In den Kommentaren findest du eine einfache ListMovies-Abfrage:
    query ListMovies @auth(level: PUBLIC) {
      movies {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
    Mit dieser Abfrage werden alle Filme und ihre Details (z. B. id, title, releaseYear) abgerufen. Die Filme werden jedoch nicht sortiert.
  2. Ersetzen Sie die vorhandene ListMovies-Abfrage durch die folgende Abfrage, um Sortier- und Begrenzungsoptionen hinzuzufügen:
    # List subset of fields for movies
    query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) {
      movies(
        orderBy: [
          { rating: $orderByRating },
          { releaseYear: $orderByReleaseYear }
        ]
        limit: $limit
      ) {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
  3. Klicken Sie auf die Schaltfläche Ausführen (lokal), um die Abfrage in Ihrer lokalen Datenbank auszuführen. Sie können die Abfragevariablen auch vor dem Ausführen im Konfigurationsbereich eingeben.
    c4d947115bb11b16.png

Zusammenfassung:

  • movies():GraphQL-Abfragefeld zum Abrufen von Filmdaten aus der Datenbank.
  • orderByRating:Parameter zum Sortieren von Filmen nach Bewertung (aufsteigend/absteigend).
  • orderByReleaseYear:Parameter zum Sortieren von Filmen nach Erscheinungsjahr (aufsteigend/absteigend).
  • limit:Damit wird die Anzahl der zurückgegebenen Filme eingeschränkt.

Abfragen in die Webanwendung einbinden

In diesem Teil des Codelabs verwenden Sie die im vorherigen Abschnitt definierten Abfragen in Ihrer Webanwendung. Die Firebase Data Connect-Emulatoren generieren SDKs basierend auf den Informationen in den .gql-Dateien (insbesondere schema.gql, queries.gql, mutations.gql) und der connector.yaml-Datei. Diese SDKs können direkt in Ihrer Anwendung aufgerufen werden.

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) oben den Kommentar aus der Importanweisung:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    Die Funktion listMovies, der Antworttyp ListMoviesData und das Enum OrderDirection sind SDKs, die von den Firebase Data Connect-Emulatoren basierend auf dem Schema und den zuvor definierten Abfragen generiert wurden .
  2. Ersetzen Sie die Funktionen handleGetTopMovies und handleGetLatestMovies durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • listMovies:Eine automatisch generierte Funktion, die die Abfrage listMovies aufruft, um eine Liste von Filmen abzurufen. Sie haben die Möglichkeit, nach Bewertung oder Erscheinungsjahr zu sortieren und die Anzahl der Ergebnisse einzuschränken.
  • ListMoviesData:Der Ergebnistyp, mit dem die Top 10 und die neuesten Filme auf der Startseite der App angezeigt werden.

Beispiele ansehen

Aktualisieren Sie Ihre Webanwendung, um die Abfrage in Aktion zu sehen. Auf der Startseite wird jetzt die Liste der Filme dynamisch angezeigt. Die Daten werden direkt aus Ihrer lokalen Datenbank abgerufen. Die meistbewerteten und neuesten Filme werden nahtlos angezeigt und spiegeln die Daten wider, die Sie gerade eingerichtet haben.

6. Film- und Schauspielerdetails anzeigen

In diesem Abschnitt implementieren Sie die Funktion zum Abrufen detaillierter Informationen zu einem Film oder Schauspieler anhand ihrer eindeutigen IDs. Dazu werden nicht nur Daten aus den jeweiligen Tabellen abgerufen, sondern auch zugehörige Tabellen zusammengeführt, um umfassende Details wie Filmrezensionen und Filmografie der Schauspieler anzuzeigen.

ac7fefa7ff779231.png

Anschlüsse implementieren

  1. Öffnen Sie dataconnect/movie-connector/queries.gql in Ihrem Projekt.
  2. Fügen Sie die folgenden Abfragen hinzu, um Film- und Schauspielerdetails abzurufen:
    # Get movie by id
    query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
    movie(id: $id) {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        description
        tags
        metadata: movieMetadatas_on_movie {
          director
        }
        mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
          id
          name
          imageUrl
        }
        supportingActors: actors_via_MovieActor(
          where: { role: { eq: "supporting" } }
        ) {
          id
          name
          imageUrl
        }
        reviews: reviews_on_movie {
          id
          reviewText
          reviewDate
          rating
          user {
            id
            username
          }
        }
      }
    }
    
    # Get actor by id
    query GetActorById($id: UUID!) @auth(level: PUBLIC) {
      actor(id: $id) {
        id
        name
        imageUrl
        mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) {
          id
          title
          genre
          tags
          imageUrl
        }
        supportingActors: movies_via_MovieActor(
          where: { role: { eq: "supporting" } }
        ) {
          id
          title
          genre
          tags
          imageUrl
        }
      }
    }
    
  3. Speichern Sie die Änderungen und überprüfen Sie die Abfragen.

Zusammenfassung:

  • movie()/actor(): GraphQL-Abfragefelder zum Abrufen eines einzelnen Films oder Schauspielers aus der Tabelle Movies oder Actors.
  • _on_: Dadurch ist der direkte Zugriff auf Felder eines verknüpften Typs mit einer Fremdschlüsselbeziehung möglich. Mit reviews_on_movie werden beispielsweise alle Rezensionen zu einem bestimmten Film abgerufen.
  • _via_: Wird verwendet, um n:m-Beziehungen über eine Join-Tabelle zu verwalten. Beispielsweise greift actors_via_MovieActor über die Join-Tabelle MovieActor auf den Typ Actor zu und die Bedingung where filtert Schauspieler nach ihrer Rolle (z. B. „Hauptrolle“ oder „Nebenrolle“).

Abfrage mit Testdaten testen

  1. Im Ausführungsbereich von Data Connect können Sie die Abfrage testen, indem Sie Mock-IDs eingeben, z. B.:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. Klicken Sie für GetMovieById auf Ausführen (lokal), um die Details zu „Quantum Paradox“ abzurufen, dem Mockfilm, auf den sich die obige ID bezieht.

1b08961891e44da2.png

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
    import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
    import { GetActorByIdData, getActorById } from "@movie/dataconnect";
    
  2. Ersetzen Sie die Funktionen handleGetMovieById und handleGetActorById durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • getMovieById / getActorById: Das sind automatisch generierte Funktionen, die die von dir definierten Abfragen aufrufen und detaillierte Informationen zu einem bestimmten Film oder Schauspieler abrufen.
  • GetMovieByIdData / GetActorByIdData: Dies sind die Ergebnistypen, mit denen Film- und Schauspielerdetails in der App angezeigt werden.

Beispiele ansehen

Rufen Sie nun die Startseite Ihrer Webanwendung auf. Wenn du auf einen Film klickst, werden alle Details angezeigt, einschließlich Schauspieler und Rezensionen. Diese Informationen werden aus verknüpften Tabellen abgerufen. Wenn du auf einen Schauspieler klickst, werden die Filme angezeigt, in denen er mitgespielt hat.

7. Nutzerauthentifizierung verarbeiten

In diesem Abschnitt implementieren Sie die Funktionen zum Anmelden und Abmelden von Nutzern mit Firebase Authentication. Außerdem verwenden Sie Firebase Authentication-Daten, um Nutzerdaten direkt in Firebase DataConnect abzurufen oder zu aktualisieren. So wird eine sichere Nutzerverwaltung in Ihrer App gewährleistet.

9890838045d5a00e.png

Anschlüsse implementieren

  1. Öffnen Sie mutations.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgende Mutation hinzu, um den aktuell authentifizierten Nutzer zu erstellen oder zu aktualisieren:
    # Create or update the current authenticated user
    mutation UpsertUser($username: String!) @auth(level: USER) {
      user_upsert(
        data: {
          id_expr: "auth.uid"
          username: $username
        }
      )
    }
    

Zusammenfassung:

  • id_expr: "auth.uid": Hier wird auth.uid verwendet, das direkt von Firebase Authentication und nicht vom Nutzer oder der App bereitgestellt wird. Dadurch wird eine zusätzliche Sicherheitsebene hinzugefügt, da die Nutzer-ID sicher und automatisch verarbeitet wird.

Aktuellen Nutzer abrufen

  1. Öffnen Sie queries.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgende Abfrage hinzu, um den aktuellen Nutzer abzurufen:
    # 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
            }
          }
        }
      }
    }
    

Zusammenfassung:

  • auth.uid: Dieser wird direkt aus Firebase Authentication abgerufen, um einen sicheren Zugriff auf nutzerspezifische Daten zu ermöglichen.
  • _on_-Felder: Diese Felder stellen die Join-Tabellen dar:
    • reviews_on_user: Hier werden alle Rezensionen abgerufen, die sich auf den Nutzer beziehen, einschließlich der id und title des Films.
    • favorite_movies_on_user: Ruft alle Filme ab, die vom Nutzer als Favoriten markiert wurden, einschließlich detaillierter Informationen wie genre, releaseYear, rating und metadata.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) die Kommentarzeichen bei den folgenden Importen:
    import { upsertUser } from "@movie/dataconnect";
    import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
    
  2. Ersetzen Sie die Funktionen handleAuthStateChange und handleGetCurrentUser durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • handleAuthStateChange: Diese Funktion überwacht Änderungen des Authentifizierungsstatus. Wenn sich ein Nutzer anmeldet, werden seine Daten festgelegt und die upsertUser-Mutation aufgerufen, um die Informationen des Nutzers in der Datenbank zu erstellen oder zu aktualisieren.
  • handleGetCurrentUser: Ruft das Profil des aktuellen Nutzers mit der Abfrage getCurrentUser ab, über die die Rezensionen und Lieblingsfilme des Nutzers abgerufen werden.

Beispiele ansehen

Klicken Sie jetzt in der Navigationsleiste auf die Schaltfläche „Über Google anmelden“. Sie können sich mit dem Firebase Authentication-Emulator anmelden. Klicken Sie nach der Anmeldung auf „Mein Profil“. Sie ist vorerst leer, aber Sie haben die Grundlage für die nutzerspezifische Datenverarbeitung in Ihrer App geschaffen.

8. Nutzerinteraktionen implementieren

In diesem Abschnitt des Codelabs implementieren Sie Nutzerinteraktionen in der Filmbewertungs-App. Insbesondere können Nutzer ihre Lieblingsfilme verwalten und Rezensionen hinterlassen oder löschen.

b3d0ac1e181c9de9.png

Nutzer Filme zu Favoriten hinzufügen lassen

In diesem Abschnitt richten Sie die Datenbank so ein, dass Nutzer Filme zu ihren Favoriten hinzufügen können.

Anschlüsse implementieren

  1. Öffnen Sie mutations.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgenden Mutationen hinzu, um Filme zu den Favoriten hinzuzufügen:
    # 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 })
    }
    
    

Zusammenfassung:

  • userId_expr: "auth.uid": Verwendet auth.uid, das direkt von Firebase Authentication bereitgestellt wird. So wird sichergestellt, dass nur auf die Daten des authentifizierten Nutzers zugegriffen oder diese nur von ihm geändert werden.

Prüfen, ob ein Film zu den Favoriten hinzugefügt wurde

  1. Öffnen Sie queries.gql in dataconnect/movie-connector/.
  2. Füge die folgende Abfrage hinzu, um zu prüfen, ob ein Film zu den Favoriten gehört:
    query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
        movieId
      }
    }
    

Zusammenfassung:

  • auth.uid: Sorgt mit Firebase Authentication für sicheren Zugriff auf nutzerspezifische Daten.
  • favorite_movie: Prüft in der Zusammenführungstabelle favorite_movies, ob ein bestimmter Film vom aktuellen Nutzer als Favorit markiert wurde.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. Ersetzen Sie die Funktionen handleAddFavoritedMovie, handleDeleteFavoritedMovie und handleGetIfFavoritedMovie durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • handleAddFavoritedMovie und handleDeleteFavoritedMovie: Mithilfe der Mutationen kannst du einen Film sicher zu den Favoriten des Nutzers hinzufügen oder daraus entfernen.
  • handleGetIfFavoritedMovie: Prüft mit der Abfrage getIfFavoritedMovie, ob ein Film vom Nutzer als Favorit markiert wurde.

Beispiele ansehen

Du kannst Filme jetzt als Favoriten hinzufügen oder die Markierung aufheben, indem du auf den Filmkarten und auf der Detailseite des Films auf das Herzsymbol klickst. Außerdem kannst du dir deine Lieblingsfilme auf deiner Profilseite ansehen.

Nutzern erlauben, Rezensionen zu verfassen oder zu löschen

Als Nächstes implementieren Sie den Bereich zum Verwalten von Nutzerrezensionen in der App.

Anschlüsse implementieren

In mutations.gql (dataconnect/movie-connector/mutations.gql): Fügen Sie die folgenden Mutationen hinzu:

# 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 })
}

Zusammenfassung:

  • userId_expr: "auth.uid": Sorgt dafür, dass Rezensionen dem authentifizierten Nutzer zugeordnet werden.
  • reviewDate_date: { today: true }: Das aktuelle Datum für die Rezension wird automatisch mit DataConnect generiert, sodass eine manuelle Eingabe nicht erforderlich ist.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
    import { addReview, deleteReview } from "@movie/dataconnect";
    
  2. Ersetzen Sie die Funktionen handleAddReview und handleDeleteReview durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • handleAddReview: Ruft die addReview-Mutation auf, um eine Rezension für den angegebenen Film hinzuzufügen und sie sicher mit dem authentifizierten Nutzer zu verknüpfen.
  • handleDeleteReview: Mit der deleteReview-Mutation wird eine Rezension für einen Film durch den authentifizierten Nutzer entfernt.

Beispiele ansehen

Nutzer können jetzt auf der Detailseite eines Films Rezensionen hinterlassen. Außerdem können sie ihre Rezensionen auf ihrer Profilseite ansehen und löschen. So haben sie die volle Kontrolle über ihre Interaktionen mit der App.

9. Erweiterte Filter und teilweise Textübereinstimmung

In diesem Abschnitt implementieren Sie erweiterte Suchfunktionen, mit denen Nutzer Filme anhand einer Reihe von Bewertungen und Erscheinungsjahren suchen, nach Genres und Tags filtern, eine teilweise Textübereinstimmung in Titeln oder Beschreibungen durchführen und sogar mehrere Filter kombinieren können, um genauere Ergebnisse zu erhalten.

ece70ee0ab964e28.png

Anschlüsse implementieren

  1. Öffnen Sie queries.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgende Abfrage hinzu, um verschiedene Suchfunktionen zu unterstützen:
    # 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
        }
      }
    }
    

Zusammenfassung:

  • Operator _and: Hiermit werden mehrere Bedingungen in einer einzigen Abfrage kombiniert, sodass die Suche nach mehreren Feldern wie releaseYear, rating und genre gefiltert werden kann.
  • contains-Operator: Es wird nach teilweisen Textübereinstimmungen in Feldern gesucht. In dieser Abfrage wird nach Übereinstimmungen in title, description, name oder reviewText gesucht.
  • where-Klausel: Gibt die Bedingungen für das Filtern von Daten an. In jedem Bereich (Filme, Schauspieler, Rezensionen) werden mit einer where-Klausel die spezifischen Kriterien für die Suche definiert.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
    import { searchAll, SearchAllData } from "@movie/dataconnect";
    
  2. Ersetzen Sie die Funktion handleSearchAll durch den folgenden Code:
    // 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;
      }
    };
    

Zusammenfassung:

  • handleSearchAll: Bei dieser Funktion wird die Abfrage searchAll verwendet, um eine Suche basierend auf der Eingabe des Nutzers durchzuführen. Die Ergebnisse werden nach Parametern wie Jahr, Bewertung, Genre und teilweisen Textübereinstimmungen gefiltert.

Beispiele ansehen

Rufe in der Web-App über die Navigationsleiste die Seite „Erweiterte Suche“ auf. Du kannst jetzt mithilfe verschiedener Filter und Eingaben nach Filmen, Schauspielern und Rezensionen suchen und detaillierte und personalisierte Suchergebnisse erhalten.

10. Optional: In der Cloud bereitstellen (Abrechen erforderlich)

Nachdem Sie die Iteration der lokalen Entwicklung durchlaufen haben, ist es an der Zeit, Ihr Schema, Ihre Daten und Abfragen auf dem Server bereitzustellen. Sie können dazu die VS Code-Erweiterung „Firebase Data Connect“ oder die Firebase CLI verwenden.

Firebase-Preismodell upgraden

Damit Sie Firebase Data Connect in Cloud SQL for PostgreSQL einbinden können, muss Ihr Firebase-Projekt den Blaze-Tarif (Pay-as-you-go) haben, d. h. mit einem Cloud-Rechnungskonto verknüpft sein.

  • Für ein Cloud-Rechnungskonto ist eine Zahlungsmethode wie eine Kreditkarte erforderlich.
  • Wenn Sie neu bei Firebase und Google Cloud sind, prüfen Sie, ob Sie Anspruch auf ein Guthaben in Höhe von 300$und ein kostenloses Cloud Billing-Konto haben.
  • Wenn Sie dieses Codelab im Rahmen einer Veranstaltung absolvieren, fragen Sie den Organisator, ob Cloud-Guthaben verfügbar ist.

So führen Sie ein Upgrade auf den Blaze-Tarif durch:

  1. Wählen Sie in der Firebase Console Tarif upgraden aus.
  2. Wählen Sie den Blaze-Tarif aus. Folgen Sie der Anleitung auf dem Bildschirm, um ein Cloud-Rechnungskonto mit Ihrem Projekt zu verknüpfen.
    Wenn Sie im Rahmen dieses Upgrades ein Cloud-Rechnungskonto erstellen mussten, müssen Sie möglicherweise zur Firebase Console zurückkehren, um das Upgrade abzuschließen.

Web-App mit Firebase-Projekt verknüpfen

  1. Registrieren Sie Ihre Webanwendung in Ihrem Firebase-Projekt über die Firebase Console:
    1. Öffnen Sie Ihr Projekt und klicken Sie auf App hinzufügen.
    2. Ignorieren Sie die SDK-Einrichtung und -Konfiguration vorerst, aber kopieren Sie das generierte firebaseConfig-Objekt.
    7030822793e4d75b.png
  2. Ersetzen Sie die vorhandene firebaseConfig in app/src/lib/firebase.tsx durch die Konfiguration, die Sie gerade aus der Firebase Console kopiert haben.
    const firebaseConfig = {
      apiKey: "API_KEY",
      authDomain: "PROJECT_ID.firebaseapp.com",
      projectId: "PROJECT_ID",
      storageBucket: "PROJECT_ID.firebasestorage.app",
      messagingSenderId: "SENDER_ID",
      appId: "APP_ID"
    };
    
  3. Webanwendung erstellen:Erstellen Sie in VS Code im Ordner app mit Vite die Webanwendung für die Bereitstellung im Hosting:
    cd app
    npm run build
    

Firebase Authentication in Ihrem Firebase-Projekt einrichten

  1. Richten Sie die Firebase Authentication mit Google Log-in ein.62af2f225e790ef6.png
  2. Optional: Erlauben Sie über die Firebase Console Domains für die Firebase Authentication (z. B. http://127.0.0.1).
    1. Klicken Sie in den Authentifizierungseinstellungen auf Autorisierte Domains.
    2. Klicken Sie auf „Domain hinzufügen“ und fügen Sie Ihre lokale Domain der Liste hinzu.

c255098f12549886.png

Mit der Firebase CLI bereitstellen

  1. Achten Sie darauf, dass die Instanz-ID, Datenbank-ID und Dienst-ID in dataconnect/dataconnect.yaml mit Ihrem Projekt übereinstimmen:
    specVersion: "v1alpha"
    serviceId: "your-service-id"
    location: "us-central1"
    schema:
      source: "./schema"
      datasource:
        postgresql:
          database: "your-database-id"
          cloudSql:
            instanceId: "your-instance-id"
    connectorDirs: ["./movie-connector"]
    
  2. Die Firebase CLI muss für Ihr Projekt eingerichtet sein:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. Führen Sie im Terminal den folgenden Befehl aus, um die Bereitstellung vorzunehmen:
    firebase deploy --only dataconnect,hosting
    
  4. Führen Sie diesen Befehl aus, um die Schemaänderungen zu vergleichen:
    firebase dataconnect:sql:diff
    
  5. Wenn die Änderungen akzeptabel sind, wenden Sie sie mit folgendem Befehl an:
    firebase dataconnect:sql:migrate
    

Ihre Cloud SQL for PostgreSQL-Instanz wird mit dem endgültig bereitgestellten Schema und den Daten aktualisiert. Sie können den Status in der Firebase Console prüfen.

Sie sollten Ihre App jetzt unter your-project.web.app/ live sehen können. Außerdem können Sie im Bereich „Firebase Data Connect“ auf Ausführen (Produktion) klicken, um der Produktionsumgebung Daten hinzuzufügen, genau wie bei den lokalen Emulatoren.

11. Optional: Vektorsuche mit Firebase Data Connect (Aktivierung der Abrechnung erforderlich)

In diesem Abschnitt aktivieren Sie die Vektorsuche in Ihrer Filmrezensions-App mit Firebase Data Connect. Mit dieser Funktion sind inhaltsbasierte Suchanfragen möglich, z. B. die Suche nach Filmen mit ähnlichen Beschreibungen mithilfe von Vektoreinbettungen.

Für diesen Schritt müssen Sie den letzten Schritt dieses Codelabs zum Bereitstellen in Google Cloud ausgeführt haben.

4b5aca5a447d2feb.png

Schema aktualisieren, um Einbettungen für ein Feld einzubeziehen

Fügen Sie in dataconnect/schema/schema.gql der Tabelle Movie das Feld descriptionEmbedding hinzu:

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
}

Zusammenfassung:

  • descriptionEmbedding: Vector @col(size:768): In diesem Feld werden die semantischen Einbettungen von Filmbeschreibungen gespeichert, wodurch eine vektorbasierte Inhaltssuche in Ihrer App möglich ist.

Vertex AI aktivieren

  1. Folgen Sie der Anleitung zu den Voraussetzungen, um Vertex AI APIs in Google Cloud einzurichten. Dieser Schritt ist für die Unterstützung der Funktionen zur Generierung von Einbettungen und zur Vektorsuche erforderlich.
  2. Bereitstellen Sie Ihr Schema noch einmal, um pgvector und die Vektorsuche zu aktivieren. Klicken Sie dazu mit der VS Code-Erweiterung „Firebase Data Connect“ auf „In Produktion bereitstellen“.

Datenbank mit Einbettungen füllen

  1. Öffnen Sie den Ordner dataconnect in VS Code.
  2. Klicken Sie in optional_vector_embed.gql auf Ausführen(lokal), um Ihre Datenbank mit Einbettungen für die Filme zu füllen.

b858da780f6ec103.png

Vektorsuchabfrage hinzufügen

Fügen Sie in dataconnect/movie-connector/queries.gql die folgende Abfrage hinzu, um Vektorsuchen durchzuführen:

# 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
  }
}

Zusammenfassung:

  • compare_embed: Gibt das Einbettungsmodell (textembedding-gecko@003) und den Eingabetext ($query) für den Vergleich an.
  • method: Gibt die Ähnlichkeitsmethode (L2) an, die der euklidischen Entfernung entspricht.
  • within: Die Suche wird auf Filme mit einer L2-Distanz von maximal 2 beschränkt. Dabei wird der Schwerpunkt auf ähnliche Inhalte gelegt.
  • limit: Die Anzahl der zurückgegebenen Ergebnisse wird auf 5 beschränkt.

Vektorsuchfunktion in Ihrer App implementieren

Nachdem Sie das Schema und die Abfrage eingerichtet haben, können Sie die Vektorsuche in die Dienstebene Ihrer App einbinden. Mit diesem Schritt können Sie die Suchanfrage über Ihre Webanwendung aufrufen.

  1. Entfernen Sie in app/src/lib/ MovieService.ts die Kommentarzeichen vor den folgenden Importen aus den SDKs. Die Abfrage funktioniert dann wie jede andere Abfrage.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. Fügen Sie die folgende Funktion hinzu, um die vektorbasierte Suche in die App einzubinden:
    // 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;
      }
    };
    

Zusammenfassung:

  • searchMoviesByDescription: Diese Funktion ruft die searchMovieDescriptionUsingL2similarity-Abfrage auf und übergibt den Eingabetext, um eine vektorbasierte Inhaltssuche durchzuführen.

Beispiele ansehen

Gehen Sie in der Navigationsleiste zum Bereich „Vector Search“ (Vektorsuche) und geben Sie Begriffe wie „romantisch und modern“ ein. Es wird eine Liste mit Filmen angezeigt, die den von dir gesuchten Inhalten entsprechen. Du kannst auch die Detailseite eines Films aufrufen und unten im Bereich „Ähnliche Filme“ nachsehen.

7b71f1c75633c1be.png

12. Fazit

Glückwunsch, Sie sollten die Web-App jetzt verwenden können. Wenn Sie mit Ihren eigenen Filmdaten experimentieren möchten, können Sie sie mit der Firebase Data Connect-Erweiterung einfügen. Dazu müssen Sie die _insert.gql-Dateien nachahmen oder sie über den Ausführungsbereich „Data Connect“ in VS Code hinzufügen.

Weitere Informationen