Getting Started With TanStack Query Firebase and Firebase Data Connect

Hassan BahatiHassan Bahati
6 min read

TanStack Query Firebase provides a set of hooks for handling asynchronous tasks with Firebase in your applications. According to the official documentation: “Firebase Data Connect is Firebase's first relational database solution for developers who want to create secure and scalable apps with Cloud SQL for PostgreSQL and type-safe mobile and web SDKs.

Getting Started

In this guide, we are going to create a react app that leverages useConnectQuery and useConnectMutation from TanStack Query Firebase to perform CRUD operations to Firebase Data Connect.

Let’s jump right in!

Step 1: Bootstrapping the app.

Firstly, we are going to setup a react app using vite. To achieve this, we are going to run the command below.

pnpm create vite firebase-dataconnect-movies

On running the above command, we shall follow through the prompts and select React with Typescript options and a project will be scaffolded and will have the files as below.

Open your terminal and while at the project root, run pnpm install to install dependencies.

Once the dependencies are successfully installed, run pnpm dev and a link will be availed through which you can view the project in your browser. This will look as below;

With that, we have successfully scaffolded our react application.

Step 2: Generating Data Connect SDK for our application.

Firebase Data Connect uses GraphQL for data modeling and definition of CRUD operations. With Firebase Data Connect and GraphQL, we are going to define schemas, queries and mutations, Data Connect will automatically generate custom SDKs for usage in the client logic.

Firstly, ensure that you have the Firebase Data Connect for VSCode Extension installed. On clicking the Firebase Data Connect Extension icon, you will see a screen as below;

Here, we shall go ahead and click the Run firebase init button which will start a couple prompts in our terminal. The very first option will be to use an existing project or create new project, for which we shall select create new project and any name of your choice and in our case we are going to use fdc-movies-app. There after a firebase project will be created.

You will then be asked what connector should be setup for a generated SDK for, we shall select firebase-dataconnect-movies/default which is the suggested default option.

When all the above are successfully completed, you would have gone through the prompts as below;

You can proceed to click enter and the current initialization terminal will close automatically.

At this point, in sidebar under the Firebase Data Connect Extension, you will have the following options

It is also important to note that a couple of files have also been generated into our project as below;

  • .firebase - folder

  • dataconnect - folder

  • dataconnect-generated - folder

  • .firebaserc - file

  • firebase.json - file

We now need to go into the dataconnect folder and uncomment all the contents of the mutations.gql, queries.gql and schema.gql files. All code in those files is initially commented out. After uncommenting, these will look as below;

At this point, the generation of the Data Connect SDK is complete and it is currently connected to your application.

Since we are going to be testing locally, we shall need to start the emulators by clicking the Start emulators button under Firebase Data Connect Extension. On clicking this button, the emulators will and the extension will show as below;

Step 3: Initialization of Firebase and TanStack Query.

In Step 2, we did create a firebase project called fdc-movies-app. When we head over to the firebase console, we should be able to find our project listed.

While in our project, we shall need to register an app. We are going to register a web app and we shall do that by selecting the Web option. We shall proceed to register our app with a name of our choice which could be fdc-movies-app and then the Firebase SDK config object will be availed as below;

We shall then head over to our project and add a .env file to store our environment variables and replace the placeholder values with the actual values from the above firebaseConfig object. Please note all these will be prefixed with VITE_ since we are using Vite.

  VITE_FIREBASE_API_KEY="your-firebase-apiKey"
  VITE_FIREBASE_AUTH_DOMAIN="your-firebase-authDomain"
  VITE_FIREBASE_PROJECT_ID="your-firebase-projectId"
  VITE_FIREBASE_STORAGE_BUCKET="your-firebase-storageBucket"
  VITE_FIREBASE_MESSAGING_SENDER_ID="your-firebase-messagingSenderId"
  VITE_FIREBASE_APP_ID="your-firebase-appId"

Next, we are going to install the firebase and @tanstack/react-query packages using the commands below;

pnpm i firebase @tanstack/react-query

We are then going to modify our src/main.tsx so as to initialize a firebase app and initialize a Data Connect emulator connection as seen below;

Step 4: CRUD operations with useConnectMutation and useConnectQuery

useConnectMutation and useConnectQuery are hooks availed by TanStack Query Firebase. useConnectMutation enables you to perform create, update and delete operations while useConnectQuery enables you to read/retrieve data records stored in your database.

To get started with TanStack Query Firebase, we shall install the library using the following command.

pnpm i @tanstack-query-firebase/react

We are going to use the useConnectQuery hook to fetch the movies data as seen below. This will be done in the src/App.tsx file.

import "./App.css";
import {useConnectQuery} from "@tanstack-query-firebase/react";
import {listMoviesRef} from "@firebasegen/default-connector";

function App() {
  const { data, isLoading, isSuccess, isError } = useConnectQuery(listMoviesRef());

return(
       <div>
          {isLoading && <p>Loading...</p>}
          {isError && <p>Sorry, an error occurred.</p>}
          {isSuccess && data.movies.length > 0 ? (
            <div className="movies-grid">
              {data.movies.map((movie, index) => (
                <div className="movie-card" key={index}>
                  <img src={movie.imageUrl} alt={movie.title} />
                  <h3>{movie.title}</h3>
                  <p>Genre: {movie.genre}</p>
                </div>
              ))}
            </div>
          ) : (
            <p>No movies found!</p>
          )}
        </div>
    )
}

export default App;

For creating movies, we are going to use the useConnectMutation hook, and we are going to set it up as below;

import "./App.css";
import {useConnectMutation} from "@tanstack-query-firebase/react";
import {createMovieRef} from "@firebasegen/default-connector";

function App() {
  const [movie, setMovie] = useState({
    title: "",
    imageUrl: "",
    genre: "",
  });

  const [error, setError] = useState("");

   const createMovieMutation = useConnectMutation(createMovieRef, {
        invalidate: [listMoviesRef()],
        onSuccess: () => {
          setMovie({ title: "", imageUrl: "", genre: "" });
        },
        onError: (error) => {
          setError("Failed to add movie. Please try again.");
          console.error(error);
        },
     });

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setMovie((prevMovie) => ({
      ...prevMovie,
      [name]: value,
    }));
  };

  const handleFormSubmit = (e: React.FormEvent) => {
    e.preventDefault();

    if (!movie.title || !movie.imageUrl || !movie.genre) {
      setError("All fields are required.");
      return;
    }

    setError("");
    createMovieMutation.mutate(movie);
  };

return(
      <div className="movie-form">
          <h2>Add a New Movie</h2>
          <form onSubmit={handleFormSubmit}>
            <div>
              <label>Title:</label>
              <input
                type="text"
                name="title"
                value={movie.title}
                onChange={handleInputChange}
                placeholder="Movie Title"
              />
            </div>
            <div>
              <label>Image URL:</label>
              <input
                type="text"
                name="imageUrl"
                value={movie.imageUrl}
                onChange={handleInputChange}
                placeholder="Movie Image URL"
              />
            </div>
            <div>
              <label>Genre:</label>
              <input
                type="text"
                name="genre"
                value={movie.genre}
                onChange={handleInputChange}
                placeholder="Movie Genre"
              />
            </div>
            <button type="submit" disabled={createMovieMutation.isPending}>
              {createMovieMutation.isPending ? "Adding..." : "Add Movie"}
            </button>
          </form>
          {error && <p className="error-message">{error}</p>}
        </div>
    )
}

export default App;

For the full code, please checkout the src/App.tsx on GitHub.

When we visit our browser, the interface will be as in the image below. Here, new movies can be added and will appear in the list right away.

Please note that the Data Connect emulator has to be running as seen in Step 2, otherwise the connection to the database will fail.

Congratulations!

References

https://github.com/HassanBahati/tanstack-query-firebase-examples/tree/main/react/firebase-dataconnect-movies

https://firebase.google.com/docs/data-connect/

https://graphql.org/learn/

0
Subscribe to my newsletter

Read articles from Hassan Bahati directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hassan Bahati
Hassan Bahati