Angular and React Bindings for Data Connect

Modern app data can get complex, and efficiently connecting your frontend application to your backend data is crucial for building responsive user experiences. Firebase Data Connect already offers a powerful way to model, query, and mutate your structured data using GraphQL schemas directly within your Firebase project. Building on that foundation, we’re excited to introduce preview bindings for Angular and React that make integrating Firebase Data Connect into your web applications smoother than ever! These bindings leverage the popular TanStack Query library (v5) to streamline data fetching, caching, and state management, letting you focus more on crafting your app’s UI and less on data plumbing.

The Core Idea: Type-Safe Data Management with TanStack Query

Consider your Data Connect schemas as the blueprint for your app’s structured data. You define types like Movie or User with specific fields and relationships in your schema.graphql (or similar .gql files):

type Movie @table(name: "Movies") {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  releaseYear: Int @index
  rating: Int
  description: String
}

Your app may also have queries in query.gql such as:

query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) {
  movies(
    orderBy: [
      { rating: $orderByRating },
      { releaseYear: $orderByReleaseYear }
    ]
    limit: $limit
  ) {
    
    id
    title
    description
  }
}

query GetActorById($id: UUID!) @auth(level: PUBLIC) {
  actor(id: $id) {
    id
    name
    imageUrl
    mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) {
      id
      title
    }
    supportingActors: movies_via_MovieActor(
      where: { role: { eq: "supporting" } }
    ) {
      id
      title
    }
  }
}

Previously, you’d use the generated Data Connect SDK functions directly, often wrapping them in services or custom hooks. The new bindings automate much of this boilerplate.

By enabling the relevant flag (react: true or angular: true) in your connector.yaml and running firebase dataconnect:sdk:generate, the bindings will:

Generate Type-Safe Hooks (React) or Injectors (Angular): Based on the queries and mutations defined in your GraphQL schema.

Integrate with TanStack Query: Provide functions that automatically wrap your Data Connect operations with TanStack Query’s useQuery, useMutation, etc.

Let’s see how to set it up.

Use Angular bindings

1. Setup

Enable Angular SDK Generation: Add angular: true under javascriptSdk in your connector.yaml:

generate:
javascriptSdk:
package: "@movie/dataconnect"
outputDir: ../../src/lib/dataconnect-sdk
packageJsonDir: "../../"
angular: true

2. Install Dependencies

Add TanStack Query for Angular and the Firebase Data Connect Angular bindings:

npm i --save @tanstack/angular-query @tanstack-query-firebase/angular

3. Provide Query Client

Configure TanStack Query in your application’s providers, typically in app.config.ts:

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query";

// Create a single QueryClient instance
const queryClient = new QueryClient();

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    // Add the TanStack Query provider
    provideTanStackQuery(queryClient),
  ]
};

4. Emulator Setup

Open the Firebase Data Connect VS Code Extension panel. Select your Firebase project. Click “Start Emulators”.

5. Usage

Use the generated injectors directly in your components:

// movie-list.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
// Generated based on your schema (adjust path relative to your component)
import { injectListMovies } from '@movie/dataconnect/angular';
import { OrderDirection } from '@movie/dataconnect'; // Generated Enums/Types
import { MovieCardComponent } from '../movie-card/movie-card.component'; // Your UI component

@Component({
  selector: 'app-movie-list',
  standalone: true,
  imports: [CommonModule, MovieCardComponent],
  template: `
    <h2>Top Rated Movies</h2>
    <!-- Use TanStack Query state signals directly in the template -->
    @if(top10Movies.isPending()) {
      <div>Loading movies...</div>
    }
    @if(top10Movies.isError()) {
      <div>Error loading movies: {{ top10Movies.error()?.message }}</div>
    }
    @if(top10Movies.data(); as movies) {
      <div class="movie-grid">
        @for(movie of movies; track movie.id) {
          <app-movie-card [movie]="movie"/>
        } @empty {
          <div>No movies found.</div>
        }
      </div>
    }
  `,
  // Add styles as needed
})

export class MovieListComponent {
  // Inject the query function directly. It returns a TanStack Query Result Signal.
  top10Movies = injectListMovies({ limit: 10, orderByRating: OrderDirection.DESC });

  // The 'top10Movies' signal contains properties like:
  // data(): TData | undefined
  // error(): TError | null
  // isPending(): boolean
  // isSuccess(): boolean
  // ...and more TanStack Query state flags.
}

Using React bindings:

1. Setup

Enable React SDK Generation: Add react: true under javascriptSdk in your connector.yaml:

generate:
javascriptSdk:
package: "@movie/dataconnect"
outputDir: ../../src/lib/dataconnect-sdk
packageJsonDir: "../../"
react: true

2. Configure TypeScript

Ensure your tsconfig.json uses modern module resolution suitable for the generated SDK:

// tsconfig.json
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler", 
  },
}

3. Install Dependencies

Add TanStack Query for React and the Firebase Data Connect React bindings:

npm i --save @tanstack/react-query @tanstack-query-firebase/react

4. Provide Query Client

Wrap your application root (or the relevant part using queries) with QueryClientProvider:

// src/main.tsx (or index.tsx, App.tsx depending on setup)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App'; // Your main App component
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import './index.css';

// Create a client instance
const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
);

5. Usage

// src/pages/ActorPage.tsx (Example component)
import React from 'react';
import { useParams } from 'react-router-dom';
// Generated based on your schema (adjust path relative to your component)
import { useGetActorById } from '@movie/dataconnect/react'; // Import the generated hook

function ActorPage() {
  const { id } = useParams<{ id: string }>(); 

  const { data: actor, isLoading, error, isSuccess } = useGetActorById(
    { id: id! }
  );

  if (isLoading) {
    return <div>Loading actor details...</div>;
  }

  if (error) {
    return <div>Error loading actor: {error.message}</div>;
  }

  if (isSuccess) {
    return (
      <div>
        <h1>{actor.name}</h1>
        <p>Bio: {actor.bio || 'N/A'}</p>
      </div>
    );
  }

  return <div>Actor not found or ID is missing.</div>;
}

export default ActorPage;

The hook (useGetActorById) returns the standard TanStack Query result object, providing loading states, error objects, and type-safe data directly from your Data Connect schema.

Why Use the New Bindings?

  • Reduced Boilerplate: Less manual setup for data fetching, loading, and error handling.
  • Type Safety: End-to-end type safety from your GraphQL schema to your component props and state.
  • Performance: Automatic caching, request deduplication, and background updates via TanStack Query.
  • Developer Experience: Work with familiar patterns (React Hooks, Angular Injectors/Signals) tailored for Data Connect.

Get Started Today!

Explore the official Firebase Data Connect documentation and the rich TanStack Query documentation (switch framework for Angular details) to learn more. We’re eager to see the amazing, data-driven applications you build with these new capabilities, and your feedback is invaluable – please share your experiences and feature requests!