10 |
11 |
12 | TanStack Query Firebase provides a set of hooks for handling asynchronous tasks with Firebase in your applications.
13 |
14 | > Looking for React Query Firebase? Check out the [old branch](https://github.com/invertase/tanstack-query-firebase/tree/react-query-firebase).
15 |
16 | ## Why use this library?
17 |
18 | When managing Firebase’s asynchronous API calls within your application, state synchronization can become cumbersome in most applications. You will commonly find yourself handling loading states, error states, and data synchronization manually.
19 |
20 | This library provides a hands-off approach to these problems, by leveraging the popular [TanStack Query](https://tanstack.com/query/latest) project. Out of the box, you get:
21 |
22 | - **Automatic Caching**: Avoid redundant Firebase calls with built-in caching.
23 | - **Out-of-the-box Synchronization**: TanStack Query keeps your UI in sync with the Firebase backend effortlessly.
24 | - **Background Updates**: Fetch and sync data seamlessly in the background without interrupting the user experience.
25 | - **Error Handling & Retries**: Get automatic retries on failed Firebase calls, with robust error handling baked in.
26 | - **Dev Tools for Debugging**: Leverage the React Query Devtools to gain insights into your data-fetching logic and Firebase interactions.
27 |
28 | By combining Firebase with TanStack Query, you can make your app more resilient, performant, and scalable, all while writing less code.
29 |
30 | ## Installation
31 |
32 | This project expects you have `firebase` installed as a peer dependency. If you haven't done so already, install `firebase`:
33 |
34 | ```bash
35 | npm i --save firebase
36 | ```
37 |
38 | Next, install specific packages for your framework of choice:
39 |
40 | ### React
41 |
42 | ```
43 | npm i --save @tanstack/react-query @tanstack-query-firebase/react
44 | ```
45 |
46 | See the [Documentation](https://invertase.docs.page/tanstack-query-firebase/react) for more information on how to use the library.
47 |
48 | ## Status
49 |
50 | The status of the following Firebase services and frameworks are as follows:
51 |
52 | - ✅ Ready for use
53 | - 🟠 Work in progress
54 | - () Not yet started
55 |
56 | | Module | React | Vue | Solid | Angular | Svelte |
57 | |----------------|:------:|:-----:|:-----:|:-------:|:------:|
58 | | analytics | | | | | |
59 | | app-check | | | | | |
60 | | auth | 🟠 | | | | |
61 | | database | | | | | |
62 | | data-connect | ✅ | | | ✅ | |
63 | | firestore | 🟠 | | | | |
64 | | firestore/lite | | | | | |
65 | | functions | | | | | |
66 | | installations | | | | | |
67 | | messaging | | | | | |
68 | | performance | | | | | |
69 | | remote-config | | | | | |
70 | | ai | | | | | |
71 |
72 | ## License
73 |
74 | - See [LICENSE](/LICENSE)
75 |
76 | ---
77 |
78 |
85 |
86 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
3 | "vcs": {
4 | "enabled": true,
5 | "clientKind": "git",
6 | "useIgnoreFile": true,
7 | "defaultBranch": "main"
8 | },
9 | "files": {
10 | "includes": [
11 | "**",
12 | "!node_modules/**",
13 | "!.pnpm-store/**",
14 | "!dist/**",
15 | "!.next/**",
16 | "!.turbo/**",
17 | "!dataconnect-sdk/**",
18 | "!coverage/**",
19 | "!.dataconnect/**"
20 | ],
21 | "ignoreUnknown": false,
22 | "experimentalScannerIgnores": [
23 | "dist",
24 | ".next",
25 | ".turbo",
26 | "dataconnect-sdk",
27 | "node_modules",
28 | "coverage",
29 | ".dataconnect"
30 | ]
31 | },
32 | "formatter": {
33 | "enabled": true,
34 | "indentStyle": "space",
35 | "indentWidth": 2,
36 | "lineWidth": 80,
37 | "lineEnding": "lf",
38 | "attributePosition": "auto"
39 | },
40 | "linter": {
41 | "enabled": true,
42 | "rules": {
43 | "recommended": true,
44 | "style": {
45 | "noNonNullAssertion": "off",
46 | "useNodejsImportProtocol": "off"
47 | },
48 | "suspicious": {
49 | "noExplicitAny": "off",
50 | "noConfusingVoidType": "off"
51 | },
52 | "complexity": {
53 | "noForEach": "off"
54 | },
55 | "a11y": {
56 | "useButtonType": "off"
57 | }
58 | }
59 | },
60 | "javascript": {
61 | "formatter": {
62 | "quoteStyle": "double",
63 | "jsxQuoteStyle": "double",
64 | "quoteProperties": "asNeeded",
65 | "trailingCommas": "all",
66 | "semicolons": "always",
67 | "arrowParentheses": "always",
68 | "bracketSpacing": true,
69 | "bracketSameLine": false
70 | }
71 | },
72 | "json": {
73 | "parser": {
74 | "allowComments": true,
75 | "allowTrailingCommas": false
76 | },
77 | "formatter": {
78 | "enabled": true,
79 | "indentStyle": "space",
80 | "indentWidth": 2,
81 | "trailingCommas": "none"
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/.guides/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "A set of guides for interacting with the generated firebase dataconnect sdk",
3 | "mcpServers": {
4 | "firebase": {
5 | "command": "npx",
6 | "args": ["-y", "firebase-tools@latest", "experimental:mcp"]
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/.guides/setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | If the user hasn't already installed the SDK, always run the user's node package manager of choice, and install the package in the directory ../package.json.
4 | For more information on where the library is located, look at the connector.yaml file.
5 |
6 | ```ts
7 | import { initializeApp } from 'firebase/app';
8 |
9 | initializeApp({
10 | // fill in your project config here using the values from your Firebase project or from the `firebase_get_sdk_config` tool from the Firebase MCP server.
11 | });
12 | ```
13 |
14 | Then, you can run the SDK as needed.
15 | ```ts
16 | import { ... } from '@dataconnect/default-connector';
17 | ```
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/.guides/usage.md:
--------------------------------------------------------------------------------
1 | # Basic Usage
2 |
3 | Always prioritize using a supported framework over using the generated SDK
4 | directly. Supported frameworks simplify the developer experience and help ensure
5 | best practices are followed.
6 |
7 |
8 |
9 |
10 |
11 | ## Advanced Usage
12 | If a user is not using a supported framework, they can use the generated SDK directly.
13 |
14 | Here's an example of how to use it with the first 5 operations:
15 |
16 | ```js
17 | import { createMovie, upsertMovie, deleteMovie, addMeta, deleteMeta, listMovies, getMovieById, getMeta } from '@dataconnect/default-connector';
18 |
19 |
20 | // Operation CreateMovie: For variables, look at type CreateMovieVars in ../index.d.ts
21 | const { data } = await CreateMovie(dataConnect, createMovieVars);
22 |
23 | // Operation UpsertMovie: For variables, look at type UpsertMovieVars in ../index.d.ts
24 | const { data } = await UpsertMovie(dataConnect, upsertMovieVars);
25 |
26 | // Operation DeleteMovie: For variables, look at type DeleteMovieVars in ../index.d.ts
27 | const { data } = await DeleteMovie(dataConnect, deleteMovieVars);
28 |
29 | // Operation AddMeta:
30 | const { data } = await AddMeta(dataConnect);
31 |
32 | // Operation DeleteMeta: For variables, look at type DeleteMetaVars in ../index.d.ts
33 | const { data } = await DeleteMeta(dataConnect, deleteMetaVars);
34 |
35 | // Operation ListMovies:
36 | const { data } = await ListMovies(dataConnect);
37 |
38 | // Operation GetMovieById: For variables, look at type GetMovieByIdVars in ../index.d.ts
39 | const { data } = await GetMovieById(dataConnect, getMovieByIdVars);
40 |
41 | // Operation GetMeta:
42 | const { data } = await GetMeta(dataConnect);
43 |
44 |
45 | ```
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/esm/index.esm.js:
--------------------------------------------------------------------------------
1 | import { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } from 'firebase/data-connect';
2 |
3 | export const connectorConfig = {
4 | connector: 'default',
5 | service: 'tanstack-query-firebase',
6 | location: 'us-central1'
7 | };
8 |
9 | export const createMovieRef = (dcOrVars, vars) => {
10 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
11 | dcInstance._useGeneratedSdk();
12 | return mutationRef(dcInstance, 'CreateMovie', inputVars);
13 | }
14 | createMovieRef.operationName = 'CreateMovie';
15 |
16 | export function createMovie(dcOrVars, vars) {
17 | return executeMutation(createMovieRef(dcOrVars, vars));
18 | }
19 |
20 | export const upsertMovieRef = (dcOrVars, vars) => {
21 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
22 | dcInstance._useGeneratedSdk();
23 | return mutationRef(dcInstance, 'UpsertMovie', inputVars);
24 | }
25 | upsertMovieRef.operationName = 'UpsertMovie';
26 |
27 | export function upsertMovie(dcOrVars, vars) {
28 | return executeMutation(upsertMovieRef(dcOrVars, vars));
29 | }
30 |
31 | export const deleteMovieRef = (dcOrVars, vars) => {
32 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
33 | dcInstance._useGeneratedSdk();
34 | return mutationRef(dcInstance, 'DeleteMovie', inputVars);
35 | }
36 | deleteMovieRef.operationName = 'DeleteMovie';
37 |
38 | export function deleteMovie(dcOrVars, vars) {
39 | return executeMutation(deleteMovieRef(dcOrVars, vars));
40 | }
41 |
42 | export const addMetaRef = (dc) => {
43 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
44 | dcInstance._useGeneratedSdk();
45 | return mutationRef(dcInstance, 'AddMeta');
46 | }
47 | addMetaRef.operationName = 'AddMeta';
48 |
49 | export function addMeta(dc) {
50 | return executeMutation(addMetaRef(dc));
51 | }
52 |
53 | export const deleteMetaRef = (dcOrVars, vars) => {
54 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
55 | dcInstance._useGeneratedSdk();
56 | return mutationRef(dcInstance, 'DeleteMeta', inputVars);
57 | }
58 | deleteMetaRef.operationName = 'DeleteMeta';
59 |
60 | export function deleteMeta(dcOrVars, vars) {
61 | return executeMutation(deleteMetaRef(dcOrVars, vars));
62 | }
63 |
64 | export const listMoviesRef = (dc) => {
65 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
66 | dcInstance._useGeneratedSdk();
67 | return queryRef(dcInstance, 'ListMovies');
68 | }
69 | listMoviesRef.operationName = 'ListMovies';
70 |
71 | export function listMovies(dc) {
72 | return executeQuery(listMoviesRef(dc));
73 | }
74 |
75 | export const getMovieByIdRef = (dcOrVars, vars) => {
76 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
77 | dcInstance._useGeneratedSdk();
78 | return queryRef(dcInstance, 'GetMovieById', inputVars);
79 | }
80 | getMovieByIdRef.operationName = 'GetMovieById';
81 |
82 | export function getMovieById(dcOrVars, vars) {
83 | return executeQuery(getMovieByIdRef(dcOrVars, vars));
84 | }
85 |
86 | export const getMetaRef = (dc) => {
87 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
88 | dcInstance._useGeneratedSdk();
89 | return queryRef(dcInstance, 'GetMeta');
90 | }
91 | getMetaRef.operationName = 'GetMeta';
92 |
93 | export function getMeta(dc) {
94 | return executeQuery(getMetaRef(dc));
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/esm/package.json:
--------------------------------------------------------------------------------
1 | {"type":"module"}
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/index.cjs.js:
--------------------------------------------------------------------------------
1 | const { queryRef, executeQuery, mutationRef, executeMutation, validateArgs } = require('firebase/data-connect');
2 |
3 | const connectorConfig = {
4 | connector: 'default',
5 | service: 'tanstack-query-firebase',
6 | location: 'us-central1'
7 | };
8 | exports.connectorConfig = connectorConfig;
9 |
10 | const createMovieRef = (dcOrVars, vars) => {
11 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
12 | dcInstance._useGeneratedSdk();
13 | return mutationRef(dcInstance, 'CreateMovie', inputVars);
14 | }
15 | createMovieRef.operationName = 'CreateMovie';
16 | exports.createMovieRef = createMovieRef;
17 |
18 | exports.createMovie = function createMovie(dcOrVars, vars) {
19 | return executeMutation(createMovieRef(dcOrVars, vars));
20 | };
21 |
22 | const upsertMovieRef = (dcOrVars, vars) => {
23 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
24 | dcInstance._useGeneratedSdk();
25 | return mutationRef(dcInstance, 'UpsertMovie', inputVars);
26 | }
27 | upsertMovieRef.operationName = 'UpsertMovie';
28 | exports.upsertMovieRef = upsertMovieRef;
29 |
30 | exports.upsertMovie = function upsertMovie(dcOrVars, vars) {
31 | return executeMutation(upsertMovieRef(dcOrVars, vars));
32 | };
33 |
34 | const deleteMovieRef = (dcOrVars, vars) => {
35 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
36 | dcInstance._useGeneratedSdk();
37 | return mutationRef(dcInstance, 'DeleteMovie', inputVars);
38 | }
39 | deleteMovieRef.operationName = 'DeleteMovie';
40 | exports.deleteMovieRef = deleteMovieRef;
41 |
42 | exports.deleteMovie = function deleteMovie(dcOrVars, vars) {
43 | return executeMutation(deleteMovieRef(dcOrVars, vars));
44 | };
45 |
46 | const addMetaRef = (dc) => {
47 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
48 | dcInstance._useGeneratedSdk();
49 | return mutationRef(dcInstance, 'AddMeta');
50 | }
51 | addMetaRef.operationName = 'AddMeta';
52 | exports.addMetaRef = addMetaRef;
53 |
54 | exports.addMeta = function addMeta(dc) {
55 | return executeMutation(addMetaRef(dc));
56 | };
57 |
58 | const deleteMetaRef = (dcOrVars, vars) => {
59 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
60 | dcInstance._useGeneratedSdk();
61 | return mutationRef(dcInstance, 'DeleteMeta', inputVars);
62 | }
63 | deleteMetaRef.operationName = 'DeleteMeta';
64 | exports.deleteMetaRef = deleteMetaRef;
65 |
66 | exports.deleteMeta = function deleteMeta(dcOrVars, vars) {
67 | return executeMutation(deleteMetaRef(dcOrVars, vars));
68 | };
69 |
70 | const listMoviesRef = (dc) => {
71 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
72 | dcInstance._useGeneratedSdk();
73 | return queryRef(dcInstance, 'ListMovies');
74 | }
75 | listMoviesRef.operationName = 'ListMovies';
76 | exports.listMoviesRef = listMoviesRef;
77 |
78 | exports.listMovies = function listMovies(dc) {
79 | return executeQuery(listMoviesRef(dc));
80 | };
81 |
82 | const getMovieByIdRef = (dcOrVars, vars) => {
83 | const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true);
84 | dcInstance._useGeneratedSdk();
85 | return queryRef(dcInstance, 'GetMovieById', inputVars);
86 | }
87 | getMovieByIdRef.operationName = 'GetMovieById';
88 | exports.getMovieByIdRef = getMovieByIdRef;
89 |
90 | exports.getMovieById = function getMovieById(dcOrVars, vars) {
91 | return executeQuery(getMovieByIdRef(dcOrVars, vars));
92 | };
93 |
94 | const getMetaRef = (dc) => {
95 | const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined);
96 | dcInstance._useGeneratedSdk();
97 | return queryRef(dcInstance, 'GetMeta');
98 | }
99 | getMetaRef.operationName = 'GetMeta';
100 | exports.getMetaRef = getMetaRef;
101 |
102 | exports.getMeta = function getMeta(dc) {
103 | return executeQuery(getMetaRef(dc));
104 | };
105 |
--------------------------------------------------------------------------------
/dataconnect-sdk/js/default-connector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@dataconnect/default-connector",
3 | "version": "1.0.0",
4 | "author": "Firebase (https://firebase.google.com/)",
5 | "description": "Generated SDK For default",
6 | "license": "Apache-2.0",
7 | "engines": {
8 | "node": " >=18.0"
9 | },
10 | "typings": "index.d.ts",
11 | "module": "esm/index.esm.js",
12 | "main": "index.cjs.js",
13 | "browser": "esm/index.esm.js",
14 | "exports": {
15 | ".": {
16 | "types": "./index.d.ts",
17 | "require": "./index.cjs.js",
18 | "default": "./esm/index.esm.js"
19 | },
20 | "./package.json": "./package.json"
21 | },
22 | "peerDependencies": {
23 | "firebase": "^10.14.0 || ^11.3.0 || ^12.0.0"
24 | }
25 | }
--------------------------------------------------------------------------------
/dataconnect/connector/connector.yaml:
--------------------------------------------------------------------------------
1 | connectorId: default
2 | generate:
3 | javascriptSdk:
4 | outputDir: ../../dataconnect-sdk/js/default-connector
5 | package: "@dataconnect/default-connector"
6 |
--------------------------------------------------------------------------------
/dataconnect/connector/mutations.gql:
--------------------------------------------------------------------------------
1 | # # Example mutations for a simple movie app
2 |
3 | # Create a movie based on user input
4 | mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
5 | @auth(level: PUBLIC) {
6 | movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
7 | }
8 |
9 | # Upsert a movie
10 | mutation UpsertMovie($id: UUID!, $title: String!, $imageUrl: String!)
11 | @auth(level: PUBLIC) {
12 | movie_upsert(data: { id: $id, title: $title, imageUrl: $imageUrl })
13 | }
14 |
15 | # Delete a movie
16 | mutation DeleteMovie($id: UUID!) @auth(level: PUBLIC) {
17 | movie_delete(id: $id)
18 | }
19 |
20 | mutation AddMeta @auth(level: PUBLIC) {
21 | ref: meta_insert(data: { ref: "" })
22 | }
23 | mutation DeleteMeta($id: UUID!) @auth(level: PUBLIC) {
24 | ref: meta_delete(id: $id)
25 | }
26 |
27 | # # Upsert (update or insert) a user's username based on their auth.uid
28 | # mutation UpsertUser($username: String!) @auth(level: USER) {
29 | # user_upsert(
30 | # data: {
31 | # id_expr: "auth.uid"
32 | # username: $username
33 | # }
34 | # )
35 | # }
36 |
37 | # # Add a review for a movie
38 | # mutation AddReview(
39 | # $movieId: UUID!
40 | # $rating: Int!
41 | # $reviewText: String!
42 | # ) @auth(level: USER) {
43 | # review_upsert(
44 | # data: {
45 | # userId_expr: "auth.uid"
46 | # movieId: $movieId
47 | # rating: $rating
48 | # reviewText: $reviewText
49 | # # reviewDate defaults to today in the schema. No need to set it manually.
50 | # }
51 | # )
52 | # }
53 |
54 | # # Logged in user can delete their review for a movie
55 | # mutation DeleteReview(
56 | # $movieId: UUID!
57 | # ) @auth(level: USER) {
58 | # review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
59 | # }
60 |
--------------------------------------------------------------------------------
/dataconnect/connector/queries.gql:
--------------------------------------------------------------------------------
1 | # # Example queries for a simple movie app.
2 |
3 | # @auth() directives control who can call each operation.
4 | # Anyone should be able to list all movies, so the auth level is set to PUBLIC
5 | query ListMovies @auth(level: PUBLIC) {
6 | movies {
7 | id
8 | title
9 | imageUrl
10 | genre
11 | }
12 | }
13 |
14 | # Get movie by id
15 | query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
16 | movie(id: $id) {
17 | id
18 | title
19 | imageUrl
20 | genre
21 | }
22 | }
23 |
24 | query GetMeta @auth(level: PUBLIC) {
25 | ref: metas {
26 | id
27 | }
28 | }
29 |
30 | # # List all users, only admins should be able to list all users, so we use NO_ACCESS
31 | # query ListUsers @auth(level: NO_ACCESS) {
32 | # users { id, username }
33 | # }
34 |
35 | # # Logged in user can list all their reviews and movie titles associated with the review
36 | # # Since the query requires the uid of the current authenticated user, the auth level is set to USER
37 | # query ListUserReviews @auth(level: USER) {
38 | # user(key: {id_expr: "auth.uid"}) {
39 | # id
40 | # username
41 | # # _on_ makes it easy to grab info from another table
42 | # # Here, we use it to grab all the reviews written by the user.
43 | # reviews: reviews_on_user {
44 | # id
45 | # rating
46 | # reviewDate
47 | # reviewText
48 | # movie {
49 | # id
50 | # title
51 | # }
52 | # }
53 | # }
54 | # }
55 |
56 | # # Search for movies, actors, and reviews
57 | # query SearchMovie(
58 | # $titleInput: String
59 | # $genre: String
60 | # ) @auth(level: PUBLIC) {
61 | # movies(
62 | # where: {
63 | # _and: [
64 | # { genre: { eq: $genre } }
65 | # { title: { contains: $titleInput } }
66 | # ]
67 | # }
68 | # ) {
69 | # id
70 | # title
71 | # genre
72 | # imageUrl
73 | # }
74 | # }
75 |
--------------------------------------------------------------------------------
/dataconnect/dataconnect.yaml:
--------------------------------------------------------------------------------
1 | specVersion: "v1beta"
2 | serviceId: "tanstack-query-firebase"
3 | location: "us-central1"
4 | schema:
5 | source: "./schema"
6 | datasource:
7 | postgresql:
8 | database: "fdcdb"
9 | cloudSql:
10 | instanceId: "tanstack-query-firebase-fdc"
11 | # schemaValidation: "COMPATIBLE"
12 | connectorDirs: ["./connector"]
13 |
--------------------------------------------------------------------------------
/dataconnect/schema/schema.gql:
--------------------------------------------------------------------------------
1 | # # Example schema for simple movie review app
2 |
3 | # # Users
4 | # # Suppose a user can leave reviews for movies
5 | # # user -> reviews is a one to many relationship,
6 | # # movie -> reviews is a one to many relationship
7 | # # movie <-> user is a many to many relationship
8 | # type User @table {
9 | # id: String! @col(name: "user_auth")
10 | # username: String! @col(name: "username", dataType: "varchar(50)")
11 | # # The following are generated by the user: User! field in the Review table
12 | # # reviews_on_user
13 | # # movies_via_Review
14 | # }
15 |
16 | # Movies
17 | type Movie @table {
18 | # The below parameter values are generated by default with @table, and can be edited manually.
19 | # implies directive `@col(name: "movie_id")`, generating a column name
20 | id: UUID! @default(expr: "uuidV4()")
21 | title: String!
22 | imageUrl: String!
23 | genre: String
24 | }
25 |
26 | # Movie Metadata
27 | # Movie - MovieMetadata is a one-to-one relationship
28 | type MovieMetadata @table {
29 | # @unique indicates a 1-1 relationship
30 | movie: Movie! @unique
31 | # movieId: UUID <- this is created by the above reference
32 | rating: Float
33 | releaseYear: Int
34 | description: String
35 | }
36 |
37 | type Meta @table {
38 | ref: String!
39 | }
40 |
41 | # # Reviews
42 | # type Review @table(name: "Reviews", key: ["movie", "user"]) {
43 | # id: UUID! @default(expr: "uuidV4()")
44 | # user: User!
45 | # movie: Movie!
46 | # rating: Int
47 | # reviewText: String
48 | # reviewDate: Date! @default(expr: "request.time")
49 | # }
50 |
--------------------------------------------------------------------------------
/docs/angular/data-connect/functions/injectDataConnectMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: injectDataConnectMutation
3 | ---
4 |
5 | `injectDataConnectMutation` is an injector designed to simplify handling mutations (creating, updating, deleting) with Firebase Data Connect.
6 |
7 | See [mutations](/angular/data-connect/mutations) for more information.
8 |
9 | ## Features
10 |
11 | - Simplifies mutation handling for create, update, and delete operations using Firebase Data Connect.
12 | - Provides type-safe handling of mutations based on your Firebase Data Connect schema.
13 | - Automatically manages pending, success, and error states for mutations.
14 | - Supports optimistic updates and caching to improve user experience and performance.
15 |
16 | ## Usage
17 |
18 | ```ts
19 | import { injectDataConnectMutation } from "@tanstack-query-firebase/angular/data-connect";
20 | import { createMovieRef } from "@your-package-name/your-connector";
21 |
22 | class AddMovieComponent {
23 | createMovie = injectDataConnectMutation(
24 | createMovieRef
25 | );
26 | addMovie() {
27 | createMovie.mutate({
28 | title: 'John Wick',
29 | genre: "Action",
30 | imageUrl: "https://example.com/image.jpg",
31 | });
32 | }
33 | return (
34 |
40 | );
41 | }
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/angular/data-connect/functions/injectDataConnectQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: injectDataConnectQuery
3 | ---
4 |
5 | `injectDataConnectQuery` is an injector designed to simplify data fetching and state management with Firebase Data Connect.
6 |
7 | See [querying](/angular/data-connect/querying) for more information.
8 |
9 | ## Features
10 |
11 | - Provides type-safe handling of queries based on the Firebase Data Connect schema.
12 | - Simplifies data fetching using Firebase Data Connect.
13 | - Automatically manages loading, success, and error states.
14 | - Supports refetching data with integrated caching.
15 |
16 | ## Usage
17 |
18 | ```ts
19 | import { injectDataConnectQuery } from '@tanstack-query-firebase/angular/data-connect';
20 | import { listMoviesRef } from "@your-package-name/your-connector";
21 |
22 | // class
23 | export class MovieListComponent {
24 | movies = injectDataConnectQuery(listMoviesRef());
25 | }
26 |
27 | // template
28 | @if (movies.isPending()) {
29 | Loading...
30 | }
31 | @if (movies.error()) {
32 | An error has occurred: {{ movies.error() }}
33 | }
34 | @if (movies.data(); as data) {
35 | @for (movie of data.movies; track movie.id) {
36 |
37 | {{movie.description}}
38 |
39 | } @empty {
40 |
No items!
41 | }
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/docs/angular/data-connect/mutations.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mutations
3 | description: Learn how to mutate data in Firebase Data Connect using the Tanstack Query Firebase injectors.
4 | ---
5 |
6 | ## Mutating Data
7 |
8 | To mutate data in Firebase Data Connect, you can either use the generated injectors, or use the `injectDataConnectMutation` injector.
9 |
10 | ```ts
11 | import { injectCreateMovie } from "@firebasegen/movies/angular";
12 |
13 | @Component({
14 | ...
15 | template: `
16 |
22 | `
23 | })
24 | class AddMovieComponent() {
25 | // Calls `injectDataConnectMutation` with the respective types.
26 | // Alternatively:
27 | // import { injectDataConnectMutation } from '@tanstack-query-firebase/angular/data-connect';
28 | // ...
29 | // createMovie = injectDataConnectMutation(createMovieRef);
30 | createMovie = injectCreateMovie();
31 | addMovie() {
32 | createMovie.mutate({
33 | title: 'John Wick',
34 | genre: "Action",
35 | imageUrl: "https://example.com/image.jpg",
36 | });
37 | }
38 | }
39 | ```
40 |
41 | Additionally, you can provide a factory function to the mutation, which will be called with the mutation variables:
42 |
43 | ```ts
44 | createMovie = injectDataConnectMutation(undefined, () => ({
45 | mutationFn: (title: string) => createMovieRef({ title, reviewDate: Date.now() })
46 | }));
47 |
48 | // ...
49 | createMovie.mutate("John Wick");
50 | ```
51 |
52 | ## Invalidating Queries
53 |
54 | The function provides an additional [mutation option](https://tanstack.com/query/latest/docs/framework/angular/reference/functions/injectMutation) called `invalidate`. This option accepts a list of query references which will be automatically invalidated when the mutation is successful.
55 |
56 | You can also provide explicit references to the invalidate array, for example:
57 |
58 | ```ts
59 | const createMovie = injectDataConnectMutation(createMovieRef, {
60 | invalidate: [getMovieRef({ id: "1" })],
61 | });
62 | ```
63 |
64 | In this case only the query reference `getMovieRef({ id: "1" })` will be invalidated.
65 |
66 | ## Overriding the mutation key
67 |
68 | ### Metadata
69 |
70 | Along with the data, the function will also return the `ref`, `source`, and `fetchTime` metadata from the mutation.
71 |
72 | ```ts
73 | const createMovie = injectDataConnectMutation(createMovieRef);
74 |
75 | await createMovie.mutateAsync({
76 | title: 'John Wick',
77 | genre: "Action",
78 | imageUrl: "https://example.com/image.jpg",
79 | });
80 |
81 | console.log(createMovie.dataConnectResult().ref);
82 | console.log(createMovie.dataConnectResult().source);
83 | console.log(createMovie.dataConnectResult().fetchTime);
84 | ```
85 |
--------------------------------------------------------------------------------
/docs/angular/data-connect/querying.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Querying
3 | description: Learn how to query data from Firebase Data Connect using the Tanstack Query Firebase injectors.
4 | ---
5 |
6 | ## Querying Data
7 |
8 | To query data from Firebase Data Connect, you can either use the generated injectors, or the `injectDataConnect` injector. This will automatically create a query key and infer the data type and variables associated with the query.
9 |
10 | ```ts
11 | import { injectListMyPosts } from '@firebasegen/posts/angular'
12 |
13 | @Component({
14 | ...
15 | template: `
16 | @if (movies.isPending()) {
17 | Loading...
18 | }
19 | @if (movies.error()) {
20 | An error has occurred: {{ movies.error() }}
21 | }
22 | @if (movies.data(); as data) {
23 | @for (movie of data.movies; track movie.id) {
24 |
25 | {{movie.description}}
26 |
27 | } @empty {
28 |
No items!
29 | }
30 | }
31 | `,
32 | })
33 | export class PostListComponent {
34 | // Calls `injectDataConnectQuery` with the respective types.
35 | // Alternatively:
36 | // import { injectDataConnectQuery } from '@tanstack-query-firebase/angular/data-connect';
37 | // ...
38 | // injectDataConnectQuery(listMoviesRef())
39 | movies = injectListMovies();
40 | }
41 | ```
42 |
43 | ### Query Options
44 |
45 | To leverage the full power of Tanstack Query, you can pass in query options to the `injectDataConnectQuery` injector, for example to refetch the query on a interval:
46 |
47 | ```ts
48 | movies = injectListMovies(
49 | {
50 | refetchInterval: 1000,
51 | }
52 | );
53 | ```
54 | The injector extends the [`injectQuery`](https://tanstack.com/query/latest/docs/framework/angular/reference/functions/injectquery) injector, so you can learn more about the available options by reading the [Tanstack Query documentation](https://tanstack.com/query/latest/docs/framework/angular/reference/functions/injectquery).
55 |
56 | ### Overriding the query key
57 |
58 | To override the query key, you can pass in a custom query key to the `injectDataConnectQuery` injector:
59 |
60 | ```ts
61 | movies = injectListMovies(
62 | listMoviesRef(),
63 | {
64 | queryKey: ['movies', '1']
65 | }
66 | );
67 | ```
68 | Note that overriding the query key could mean your query is no longer synchronized with mutation invalidations or server side rendering pre-fetching.
69 |
70 | ### Metadata
71 |
72 | Along with the data, the injector will also return the `ref`, `source`, and `fetchTime` metadata from the query.
73 |
74 | ```ts
75 | const movies = injectListMovies();
76 |
77 | console.log(movies.dataConnectResult()?.ref);
78 | console.log(movies.dataConnectResult()?.source);
79 | console.log(movies.dataConnectResult()?.fetchTime);
80 | ```
81 |
82 |
--------------------------------------------------------------------------------
/docs/angular/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Angular
3 | description: Using TanStack Query Firebase with Angular
4 | ---
5 |
6 | To get started using TanStack Query Firebase with Angular, you will need to install the following packages:
7 |
8 | ```bash
9 | npm i --save firebase @tanstack/angular-query-experimental @tanstack-query-firebase/angular
10 | ```
11 |
12 | Both `@angular/fire` and `@tanstack/angular-query-experimental` are peer dependencies of `@tanstack-query-firebase/angular`.
13 |
14 | ## Usage
15 |
16 | TanStack Query Firebase provides a hands-off approach to integrate with TanStack Query - you are
17 | still responsible for both setting up Firebase in your application and configuring TanStack Query.
18 |
19 | If you haven't already done so, [initialize your Firebase project](https://firebase.google.com/docs/web/setup)
20 | and [configure TanStack Query](https://tanstack.com/query/latest/docs/framework/angular/quick-start):
21 |
22 | ### Automatic Setup
23 | To automatically set up AngularFire, just run:
24 | ```shell
25 | ng add @angular/fire
26 | ```
27 |
28 | ### Manual Setup
29 |
30 | ```ts
31 | // app.config.ts
32 | import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
33 | import { getDataConnect, provideDataConnect } from '@angular/fire/data-connect';
34 | import { connectorConfig } from '@firebasegen/movies';
35 | import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental';
36 |
37 |
38 | export const appConfig: ApplicationConfig = {
39 | providers: [
40 | ...
41 | provideFirebaseApp(() =>
42 | initializeApp(/*Replace with your firebase config*/)
43 | ),
44 | provideDataConnect(() => getDataConnect(connectorConfig)),
45 | provideTanStackQuery(new QueryClient()),
46 | ],
47 | };
48 | ```
49 |
50 | And be sure to add `angular: true` to your `connector.yaml`:
51 |
52 | ```yaml
53 | generate:
54 | javascriptSdk:
55 | angular: true
56 | outputDir: "../movies-generated"
57 | package: "@movie-app/movies"
58 | packageJsonDir: "../../"
59 | ```
60 |
61 | ## Example Usage
62 |
63 | Next, you can start to use injectors provided by `@tanstack-query-firebase/angular`. For example, to
64 | fetch a query from Data Connect:
65 |
66 | ```ts
67 | import { injectListMovies } from '@firebasegen/movies/angular'
68 |
69 | // class
70 | export class MovieListComponent {
71 | movies = injectListMovies();
72 | }
73 |
74 | // template
75 | @if (movies.isPending()) {
76 | Loading...
77 | }
78 | @if (movies.error()) {
79 | An error has occurred: {{ movies.error() }}
80 | }
81 | @if (movies.data(); as data) {
82 | @for (movie of data.movies; track movie.id) {
83 |
84 | {{movie.description}}
85 |
86 | } @empty {
87 |
No items!
88 | }
89 | }
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: TanStack Query Firebase
3 | description: TanStack Query Firebase provides a set of hooks for handling asynchronous tasks with Firebase in your applications.
4 | ---
5 |
6 | When managing Firebase's asynchronous API calls within your application, state synchronization can become cumbersome in most applications. You will commonly find yourself handling loading states, error states, and data synchronization manually.
7 |
8 | This library provides a hands-off approach to these problems, by leveraging the popular [TanStack Query](https://tanstack.com/query/latest) project. Out of the box, you get:
9 |
10 | - **Automatic Caching**: Avoid redundant Firebase calls with built-in caching.
11 | - **Out-of-the-box Synchronization**: TanStack Query keeps your UI in sync with the Firebase backend effortlessly.
12 | - **Background Updates**: Fetch and sync data seamlessly in the background without interrupting the user experience.
13 | - **Error Handling & Retries**: Get automatic retries on failed Firebase calls, with robust error handling baked in.
14 | - **Dev Tools for Debugging**: Leverage the React Query Devtools to gain insights into your data-fetching logic and Firebase interactions.
15 |
16 | By combining Firebase with TanStack Query, you can make your app more resilient, performant, and scalable, all while writing less code.
17 |
18 | Looking for React Query Firebase? Check out the [old branch](https://github.com/invertase/tanstack-query-firebase/tree/react-query-firebase).
19 |
20 | ## Framework Installation
21 |
22 | This project aims to support hooks for all major frameworks which TanStack supports; React, Vue, Solid, Angular, and Svelte. Development is still in progress, with an initial focus on React.
23 |
24 | To get started with your framework of choice, view the following documentation:
25 |
26 | - [React Documentation](/react)
27 |
28 |
--------------------------------------------------------------------------------
/docs/react-query-firebase.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating to TanStack Query Firebase
3 | description: Migrating from the old React Query Firebase to the new TanStack Query Firebase.
4 | ---
5 |
6 | The initial version of this project was called `React Query Firebase`, and built upon the
7 | older versions of *React Query*. Over the past couple of years, there's been many changes
8 | to the React Query library.
9 |
10 | The most substantial change was renaming the libray from *React Query* to *TanStack Query*.
11 |
12 | The change brought about support for a wide array of framework support beyond React, including
13 | Vue, Solid, Angular, and Svelte. The API has also evolved during this time, with many improvements
14 | and new features.
15 |
16 | The Firebase API also evolved during this time, with new services such as Data Connect and the migration
17 | from the compat API to the modular API.
18 |
19 | ## react-query-firebase
20 |
21 | The `react-query-firebase` package was built to support React only, and was tightly coupled to
22 | the older versions of React Query. For example, the `react-query-firebase` NPN namespace allowed you
23 | to install a package per Firebase service, such as `@react-query-firebase/firestore`.
24 |
25 | Additionally, the API was designed to work with the older React Query API of supporting positional args
26 | vs the newer object-based API:
27 |
28 | ```tsx
29 | useFirestoreQuery(["products"]);
30 | // vs
31 | useFirestoreQuery({ queryKey: ["products"] });
32 | ```
33 |
34 | ## tanstack-query-firebase
35 |
36 | The `tanstack-query-firebase` package is built to support all frameworks which TanStack Query supports,
37 | although initially only React is supported.
38 |
39 | Altough still in development, the API is designed to work with the newer object-based API of TanStack Query,
40 | and also supports newer Firebase services such as Data Connect.
41 |
42 | ### Realtime Subscription Issues
43 |
44 | Firebase supports realtime event subscriptions for many of its services, such as Firestore, Realtime Database and
45 | Authentication.
46 |
47 | The `react-query-firebase` package had a [limitation](https://github.com/invertase/tanstack-query-firebase/issues/25) whereby the hooks
48 | would not resubscribe whenever a component re-mounted.
49 |
50 | The initial version of `tanstack-query-firebase` currently opts-out of any realtime subscription hooks. This issue will be re-addressed
51 | once the core API is stable supporting all Firebase services.
52 |
53 | ## Migration Steps
54 |
55 | Follow the steps below to migrate your application from `react-query-firebase` to `tanstack-query-firebase`:
56 |
57 | ### 1. Install the new packages
58 |
59 | Due to the restructure of the package namespace, you will need to install the new packages:
60 |
61 | ```bash
62 | npm i --save firebase @tanstack/react-query @tanstack-query-firebase/react
63 | ```
64 |
65 | Remove any existing `@react-query-firebase/*` packages from your `package.json`.
66 |
67 | ### 2. Update your imports
68 |
69 | Update any imports for your `react-query-firebase` hooks to the new `tanstack-query-firebase` hooks, for example for Firestore:
70 |
71 | ```diff
72 | - import { useFirestoreDocument } from '@react-query-firebase/firestore';
73 | + import { useDocumentQuery } from '@tanstack-query-firebase/react/firestore';
74 | ```
75 |
76 | ### 3. Update your usage
77 |
78 | The older API followed the positional args pattern, whereas the new API follows the object-based pattern. Update your hooks to use the new pattern:
79 |
80 | ```diff
81 | - useFirestoreDocument(["products"], ref);
82 | + useDocumentQuery(ref, {
83 | + queryKey: ["products"],
84 | + });
85 | ```
86 |
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useDeleteUserMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDeleteUserMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useReloadMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useReloadMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useSendSignInLinkToEmailMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSendSignInLinkToEmailMutation
3 | ---
4 |
5 | Send a sign-in link to a user's email address.
6 |
7 | ## Usage
8 |
9 | ```jsx
10 | import { useSendSignInLinkToEmailMutation } from "@tanstack-query-firebase/react/auth";
11 | import { auth } from "../firebase";
12 |
13 | function Component() {
14 | const mutation = useSendSignInLinkToEmailMutation(auth, {
15 | onSuccess: () => {
16 | console.log("Sign-in link sent successfully!");
17 | },
18 | });
19 |
20 | return (
21 |
27 | );
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useSignInAnonymouslyMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSignInAnonymouslyMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useSignInWithCredentialMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSignInWithCredentialMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useSignInWithEmailAndPasswordMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSignInWithEmailAndPasswordMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useSignOutMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useSignOutMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useUpdateCurrentUserMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useUpdateCurrentUserMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/hooks/useVerifyPasswordResetCodeMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useVerifyPasswordResetCodeMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/auth/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Firebase Authentication
3 | ---
4 |
5 | ## Setup
6 |
7 | Before using the Tanstack Query Firebase hooks for Authentication, ensure you have configured your Firebase application
8 | to setup an Auth instance:
9 |
10 | ```ts
11 | import { initializeApp } from "firebase/app";
12 | import { getAuth } from "firebase/auth";
13 |
14 | // Initialize your Firebase app
15 | initializeApp({ ... });
16 |
17 | // Get the Auth instance
18 | const auth = getAuth(app);
19 | ```
20 |
21 | ## Importing
22 |
23 | The package exports are available via the `@tanstack-query-firebase/react` package under the `auth` namespace:
24 |
25 | ```ts
26 | import { useSignOutMutation } from "@tanstack-query-firebase/react/auth";
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/react/data-connect/hooks/useDataConnectMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDataConnectMutation
3 | ---
4 |
5 | `useDataConnectMutation` is a hook designed to simplify handling mutations (creating, updating, deleting) with Firebase Data Connect.
6 |
7 | See [mutations](/react/data-connect/mutations) for more information.
8 |
9 | ## Features
10 |
11 | - Simplifies mutation handling for create, update, and delete operations using Firebase Data Connect.
12 | - Provides type-safe handling of mutations based on your Firebase Data Connect schema.
13 | - Automatically manages pending, success, and error states for mutations.
14 | - Supports optimistic updates and caching to improve user experience and performance.
15 |
16 | ## Usage
17 |
18 | ```jsx
19 | import { useDataConnectQuery } from "@tanstack-query-firebase/react/data-connect";
20 | import { createMovieRef } from "@your-package-name/your-connector";
21 |
22 | function Component() {
23 | const { mutate, isPending, isSuccess, isError, error } =
24 | useDataConnectMutation(createMovieRef);
25 |
26 | const handleFormSubmit = (e: React.FormEvent) => {
27 | e.preventDefault();
28 | const data = new FormData(e.target as HTMLFormElement);
29 |
30 | mutate({
31 | title: data.get("title") as string,
32 | imageUrl: data.get("imageUrl") as string,
33 | genre: data.get("genre") as string,
34 | });
35 | };
36 |
37 | if (isPending) return
Adding movie...
;
38 |
39 | if (isError) return
Error: {error.message}
;
40 |
41 | return (
42 |
43 | {isSuccess &&
Movie added successfully!
}
44 |
54 |
55 | );
56 | }
57 | ```
58 |
--------------------------------------------------------------------------------
/docs/react/data-connect/hooks/useDataConnectQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDataConnectQuery
3 | ---
4 |
5 | `useDataConnectQuery` is a hook designed to simplify data fetching and state management with Firebase Data Connect.
6 |
7 | See [querying](/react/data-connect/querying) for more information.
8 |
9 | ## Features
10 |
11 | - Provides type-safe handling of queries based on the Firebase Data Connect schema.
12 | - Simplifies data fetching using Firebase Data Connect.
13 | - Automatically manages loading, success, and error states.
14 | - Supports refetching data with integrated caching.
15 |
16 | ## Usage
17 |
18 | ```jsx
19 | import { useDataConnectQuery } from "@tanstack-query-firebase/react/data-connect";
20 | import { listMoviesQuery } from "@your-package-name/your-connector";
21 |
22 | function Component() {
23 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
24 | listMoviesQuery()
25 | );
26 |
27 | if (isPending) return
Loading...
;
28 | if (isError) return
Error: {error.message}
;
29 |
30 | return (
31 |
32 | {isSuccess && (
33 |
34 | {data.movies.map((movie) => (
35 |
{movie.title}
36 | ))}
37 |
38 | )}
39 |
40 | );
41 | }
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/react/data-connect/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Firebase Data Connect
3 | ---
4 |
5 | Firebase Data Connect is a relational database service for mobile and web apps that lets you build and scale using a fully-managed PostgreSQL database powered by Cloud SQL. It provides secure schema, query and mutation management using GraphQL technology that integrates well with Firebase Authentication.
6 |
7 | To get started, ensure you have setup your Firebase project and have the Data Connect setup in your project. To learn more,
8 | follow the [Firebase Data Connect documentation](https://firebase.google.com/docs/data-connect/quickstart).
9 |
10 | ## Setup
11 |
12 | Before using the Tanstack Query Firebase hooks for Data Connect, ensure you have configured your application using your chosen connector:
13 |
14 | ```ts
15 | import { connectorConfig } from "../../dataconnect/default-connector";
16 | import { initializeApp } from "firebase/app";
17 | import { getDataConnect } from "firebase/data-connect";
18 |
19 | // Initialize your Firebase app
20 | initializeApp({ ... });
21 |
22 | // Get the Data Connect instance
23 | const dataConnect = getDataConnect(connectorConfig);
24 |
25 | // Optionally, connect to the Data Connect Emulator
26 | connectDataConnectEmulator(dataConnect, "localhost", 9399);
27 | ```
28 |
29 | ## Importing
30 |
31 | The package exports are available via the `@tanstack-query-firebase/react` package under the `data-connect` namespace:
32 |
33 | ```ts
34 | import { useDataConnectQuery } from "@tanstack-query-firebase/react/data-connect";
35 | ```
36 |
37 | ## Basic Usage
38 |
39 | To use the Tanstack Query Firebase hooks for Data Connect, you can use the `useDataConnectQuery` hook to fetch data from the database:
40 |
41 | ```tsx
42 | import { useDataConnectQuery } from "@tanstack-query-firebase/react/data-connect";
43 | import { listMoviesRef } from "../../dataconnect/default-connector";
44 |
45 | function Component() {
46 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
47 | listMoviesRef()
48 | );
49 |
50 | if (isPending) return
Loading...
;
51 |
52 | if (isError) return
Error: {error.message}
;
53 |
54 | return
{isSuccess &&
{data.movies.map((movie) =>
{movie.title}
)}
}
;
55 | }
56 | ```
57 |
58 | The hooks will automatically infer the data type from the connector and the query and automatically create a [query key](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for the query.
59 |
60 | ## Learning more
61 |
62 | To learn more about the Data Connect hooks, check out the following pages:
63 |
64 | - [Querying](/react/data-connect/querying)
65 | - [Mutations](/react/data-connect/mutations)
66 | - [Server Side Rendering](/react/data-connect/server-side-rendering)
67 |
68 |
--------------------------------------------------------------------------------
/docs/react/data-connect/mutations.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mutations
3 | description: Learn how to mutate data in Firebase Data Connect using the Tanstack Query Firebase hooks.
4 | ---
5 |
6 | ## Mutating Data
7 |
8 | To mutate data in Firebase Data Connect, you can use the `useDataConnectMutation` hook.
9 |
10 | ```tsx
11 | import { useDataConnectMutation } from "@tanstack-query-firebase/react/data-connect";
12 | import { createMovieRef } from "@dataconnect/default-connector";
13 |
14 | function Component() {
15 | const createMovie = useDataConnectMutation(
16 | createMovieRef
17 | );
18 |
19 | return (
20 |
32 | );
33 | }
34 | ```
35 |
36 | Additionally, you can provide a factory function to the mutation, which will be called with the mutation variables:
37 |
38 | ```tsx
39 | const createMovie = useDataConnectMutation((title: string) => createMovieRef({ title, reviewDate: Date.now() }));
40 | // ...
41 | createMovie.mutate("John Wick");
42 | ```
43 |
44 | ## Invalidating Queries
45 |
46 | The hook provides an additional [mutation option](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation) called `invalidate`. This option accepts a list of query references which will be automatically invalidated when the mutation is successful.
47 |
48 | ```tsx
49 | const createMovie = useDataConnectMutation(createMovieRef, {
50 | invalidate: [getMovieRef],
51 | });
52 | ```
53 |
54 | ### Implicit references
55 |
56 | The above example provides a `getMovieRef` instance to the invalidate array. By default this will invalidate all queries that cached via the `getMovieRef` reference, for example the following query references will be invalidated:
57 |
58 | ```tsx
59 | getMovieRef({ id: "1"});
60 | getMovieRef({ id: "2"});
61 | ```
62 |
63 | ### Explicit references
64 |
65 | You can also provide explicit references to the invalidate array, for example:
66 |
67 | ```tsx
68 | const createMovie = useDataConnectMutation(createMovieRef, {
69 | invalidate: [getMovieRef({ id: "1" })],
70 | });
71 | ```
72 |
73 | In this case only the query reference `getMovieRef({ id: "1" })` will be invalidated.
74 |
75 | ## Overriding the mutation key
76 |
77 | ### Metadata
78 |
79 | Along with the data, the hook will also return the `ref`, `source`, and `fetchTime` metadata from the mutation.
80 |
81 | ```tsx
82 | const createMovie = useDataConnectMutation(createMovieRef);
83 |
84 | const { dataConnectResult } = await createMovie.mutateAsync({
85 | title: 'John Wick',
86 | genre: "Action",
87 | imageUrl: "https://example.com/image.jpg",
88 | });
89 |
90 | console.log(dataConnectResult.ref);
91 | console.log(dataConnectResult.source);
92 | console.log(dataConnectResult.fetchTime);
93 | ```
--------------------------------------------------------------------------------
/docs/react/data-connect/querying.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Querying
3 | description: Learn how to query data from Firebase Data Connect using the Tanstack Query Firebase hooks.
4 | ---
5 |
6 | ## Querying Data
7 |
8 | To query data from Firebase Data Connect, you can use the `useDataConnectQuery` hook. This hook will automatically infer the data type from the connector and the query and automatically create a [query key](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for the query.
9 |
10 | ```tsx
11 | import { useDataConnectQuery } from "@tanstack-query-firebase/react/data-connect";
12 | import { listMoviesRef } from "@dataconnect/default-connector";
13 |
14 | function Component() {
15 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
16 | listMoviesRef()
17 | );
18 | }
19 | ```
20 |
21 | ### Query options
22 |
23 | To leverage the full power of Tanstack Query, you can pass in query options to the `useDataConnectQuery` hook, for example to refetch the query on a interval:
24 |
25 | ```tsx
26 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
27 | listMoviesRef(),
28 | {
29 | refetchInterval: 1000,
30 | }
31 | );
32 | ```
33 |
34 | The hook extends the [`useQuery`](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery) hook, so you can learn more about the available options by reading the [Tanstack Query documentation](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery).
35 |
36 | ### Overriding the query key
37 |
38 | To override the query key, you can pass in a custom query key to the `useDataConnectQuery` hook:
39 |
40 | ```tsx
41 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
42 | getMovieRef({ id: "1" }),
43 | {
44 | queryKey: ["movies", "1"],
45 | }
46 | );
47 | ```
48 |
49 | Note that overriding the query key could mean your query is no longer synchronized with mutation invalidations or server side rendering pre-fetching.
50 |
51 | ### Initial data
52 |
53 | If your application has already fetched a data from Data Connect, you can instead pass the `QueryResult` instance to the hook. This will instead set the `initialData` option on the hook:
54 |
55 | ```tsx
56 | // Elsewhere in your application
57 | const movies = await executeQuery(listMoviesRef());
58 |
59 | // ...
60 |
61 | function Component(props: { movies: QueryResult }) {
62 | const { data, isPending, isSuccess, isError, error } = useDataConnectQuery(
63 | props.movies
64 | );
65 | }
66 | ```
67 |
68 | The hook will immediately have data available, and immediately refetch the data when the component is mounted. This behavior can be contolled by providing a `staleTime` value to the hook or Query Client.
69 |
70 | ### Metadata
71 |
72 | Along with the data, the hook will also return the `ref`, `source`, and `fetchTime` metadata from the query.
73 |
74 | ```tsx
75 | const { dataConnectResult } = useDataConnectQuery(listMoviesRef());
76 |
77 | console.log(dataConnectResult?.ref);
78 | console.log(dataConnectResult?.source);
79 | console.log(dataConnectResult?.fetchTime);
80 | ```
81 |
--------------------------------------------------------------------------------
/docs/react/data-connect/server-side-rendering.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Server Side Rendering
3 | description: Learn how to use Tanstack Query Firebase hooks for server side rendering.
4 | ---
5 |
6 | ## Server Side Rendering
7 |
8 | The Data Connect package provides a `DataConnectQueryClient` class that extends the `QueryClient` class. This class provides an additional method called `prefetchDataConnectQuery` that allows you to prefetch data for server side rendering.
9 |
10 | ## Server Side Rendering (with Next.js)
11 |
12 | Using [traditional server side rendering](https://tanstack.com/query/latest/docs/framework/react/guides/ssr), the query client instance can be passed to the client "dehydrated", and then rehydrated on the client side.
13 |
14 | The following example demonstrates how to do this with the `DataConnectQueryClient` class and a Next.js application. The data will be immediately available to the client, and the query client will be hydrated on the client side.
15 |
16 | > Ensure you have followed the [initial setup](https://tanstack.com/query/latest/docs/framework/react/guides/ssr#initial-setup) steps first!
17 |
18 | ```tsx
19 | import type { InferGetStaticPropsType } from "next";
20 | import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
21 | import { listMoviesRef } from "@dataconnect/default-connector";
22 | import { DataConnectQueryClient } from "@tanstack-query-firebase/react/data-connect";
23 |
24 | export async function getStaticProps() {
25 | const queryClient = new DataConnectQueryClient();
26 |
27 | // Prefetch the list of movies
28 | await queryClient.prefetchDataConnectQuery(listMoviesRef());
29 |
30 | return {
31 | props: {
32 | dehydratedState: dehydrate(queryClient),
33 | },
34 | };
35 | }
36 |
37 | export default function MoviesRoute({
38 | dehydratedState,
39 | }: InferGetStaticPropsType) {
40 | return (
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | function Movies() {
48 | const movies = useDataConnectQuery(listMoviesRef());
49 |
50 | if (movies.isLoading) {
51 | return
67 | );
68 | }
69 | ```
70 |
71 | ### React Server Components (RSC)
72 |
73 | If you are oping into using React Server Components, you can similarly use the `DataConnectQueryClient` class to prefetch data within your server component:
74 |
75 | ```tsx
76 | import { Movies } from "@/examples/data-connect";
77 | import { listMoviesRef } from "@dataconnect/default-connector";
78 | import { DataConnectQueryClient } from "@tanstack-query-firebase/react/data-connect";
79 | import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
80 |
81 | import "@/firebase";
82 |
83 | export default async function PostsPage() {
84 | const queryClient = new DataConnectQueryClient();
85 |
86 | // Prefetch the list of movies
87 | await queryClient.prefetchDataConnectQuery(listMoviesRef());
88 |
89 | return (
90 |
91 |
92 |
93 | );
94 | }
95 |
96 | function Movies() {
97 | const movies = useDataConnectQuery(listMoviesRef());
98 |
99 | // ...
100 | }
101 | ```
102 |
103 | ### Gotchas
104 |
105 | - If you opt-in to providing a custom `queryKey` to either the prefetched data or the `useDataConnectQuery` hook, you must ensure that the `queryKey` is the same for both.
106 | - By default, the client will always refetch data in the background. If this behaviour is not desired, you can set the `staleTime` option in your Query Client or hook options.
107 |
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useClearIndexedDbPersistenceMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useClearIndexedDbPersistenceMutation
3 | ---
4 |
5 | A mutation which wraps the [`clearIndexedDbPersistence`](https://firebase.google.com/docs/reference/js/firestore_.md#clearindexeddbpersistence_231a8e0) function.
6 |
7 | ## Usage
8 |
9 | ```jsx
10 | import { getFirestore } from 'firebase/firestore';
11 | import { clearIndexedDbPersistence } from '@tanstack-query-firebase/react/firestore';
12 |
13 | // Get a Firestore instance using the initialized Firebase app instance
14 | const firestore = getFirestore(app);
15 |
16 | function Component() {
17 | const mutation = useClearIndexedDbPersistenceMutation(firestore);
18 |
19 | return (
20 |
23 | );
24 | }
25 | ```
26 |
27 | ## Mutation Options
28 |
29 | The hook also accepts the [`useMutation`](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation)
30 | options, for example:
31 |
32 | ```tsx
33 | const mutation = useClearIndexedDbPersistenceMutation(firestore, {
34 | onSuccess() {
35 | console.log('IndexedDB persistence cleared');
36 | },
37 | onError(error) {
38 | console.error('Failed to clear IndexedDB persistence', error);
39 | },
40 | });
41 | ```
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useCollectionQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useCollectionQuery
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useDisableNetworkMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDisableNetworkMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useDocumentQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useDocumentQuery
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useEnableNetworkMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useEnableNetworkMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useGetAggregateFromServerQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useGetAggregateFromServerQuery
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useGetCountFromServerQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useGetCountFromServerQuery
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useRunTransactionMutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useRunTransactionMutation
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/hooks/useWaitForPendingWritesQuery.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useWaitForPendingWritesQuery
3 | ---
4 |
5 | This hook is implemented, but the documentation is a work in progress, check back soon.
--------------------------------------------------------------------------------
/docs/react/firestore/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Firebase Firestore
3 | ---
4 |
5 | ## Setup
6 |
7 | Before using the Tanstack Query Firebase hooks for Firestore, ensure you have configured your Firebase application
8 | to setup an Firestore instance:
9 |
10 | ```ts
11 | import { initializeApp } from "firebase/app";
12 | import { getFirestore } from "firebase/firestore";
13 |
14 | // Initialize your Firebase app
15 | initializeApp({ ... });
16 |
17 | // Get the Firestore instance
18 | const firestore = getFirestore(app);
19 | ```
20 |
21 | ## Importing
22 |
23 | The package exports are available via the `@tanstack-query-firebase/react` package under the `firestore` namespace:
24 |
25 | ```ts
26 | import { useDocumentQuery } from "@tanstack-query-firebase/react/firestore";
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/react/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: React
3 | description: Using TanStack Query Firebase with React
4 | ---
5 |
6 | To get started using TanStack Query Firebase with React, you will need to install the following packages:
7 |
8 | ```bash
9 | npm i --save firebase @tanstack/react-query @tanstack-query-firebase/react
10 | ```
11 |
12 | Both `firebase` and `@tanstack/react-query` are peer dependencies of `@tanstack-query-firebase/react`.
13 |
14 | ## Usage
15 |
16 | TanStack Query Firebase provides a hands-off approach to integrate with TanStack Query - you are
17 | still responsible for both setting up Firebase in your application and configuring TanStack Query.
18 |
19 | If you haven't already done so, [initialize your Firebase project](https://firebase.google.com/docs/web/setup)
20 | and [configure TanStack Query](https://tanstack.com/query/latest/docs/framework/react/quick-start):
21 |
22 | ```jsx
23 | import { initializeApp } from 'firebase/app';
24 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
25 |
26 | // TODO: Replace the following with your app's Firebase project configuration
27 | const firebaseConfig = {
28 | //...
29 | };
30 |
31 | // Initialize Firebase
32 | const app = initializeApp(firebaseConfig);
33 |
34 | // Create a TanStack Query client instance
35 | const queryClient = new QueryClient()
36 |
37 | function App() {
38 | return (
39 | // Provide the client to your App
40 |
41 |
42 |
43 | )
44 | }
45 |
46 | render(, document.getElementById('root'))
47 | ```
48 |
49 | Next, you can start to use the hooks provided by `@tanstack-query-firebase/react`. For example, to
50 | fetch a document from Firestore:
51 |
52 | ```jsx
53 | import { getFirestore, doc } from 'firebase/firestore';
54 | import { useDocumentQuery } from '@tanstack-query-firebase/react/firestore';
55 |
56 | // Get a Firestore instance using the initialized Firebase app instance
57 | const firestore = getFirestore(app);
58 |
59 | function MyApplication() {
60 | // Create a document reference using Firestore
61 | const docRef = doc(firestore, 'cities', 'SF');
62 |
63 | // Fetch the document using the useDocumentQuery hook
64 | const query = useDocumentQuery(docRef);
65 |
66 | if (query.isLoading) {
67 | return
;
72 | }
73 |
74 | // The successful result of the query is a DocumentSnapshot from Firebase
75 | const snapshot = query.data;
76 |
77 | if (!snapshot.exists()) {
78 | return