Skip to content

Commit 8296f8e

Browse files
committed
Add Firebase integration
1 parent 0f1c37d commit 8296f8e

File tree

18 files changed

+6239
-77
lines changed

18 files changed

+6239
-77
lines changed

examples/react/todo/firebase.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"emulators": {
3+
"firestore": {
4+
"port": 8080
5+
},
6+
"ui": {
7+
"enabled": true,
8+
"port": 4002
9+
},
10+
"singleProjectMode": true
11+
}
12+
}

examples/react/todo/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"version": "0.0.38",
55
"dependencies": {
66
"@tanstack/electric-db-collection": "^0.0.14",
7+
"@tanstack/firebase-db-collection": "file:../../../packages/firebase-db-collection",
78
"@tanstack/query-core": "^5.75.7",
89
"@tanstack/query-db-collection": "^0.0.14",
910
"@tanstack/react-db": "^0.0.32",
@@ -14,6 +15,7 @@
1415
"drizzle-orm": "^0.40.1",
1516
"drizzle-zod": "^0.7.0",
1617
"express": "^4.19.2",
18+
"firebase": "^11.2.0",
1719
"postgres": "^3.4.7",
1820
"react": "^19.1.0",
1921
"react-dom": "^19.1.0",
@@ -39,6 +41,7 @@
3941
"eslint": "^9.22.0",
4042
"eslint-plugin-react-hooks": "^5.2.0",
4143
"eslint-plugin-react-refresh": "^0.4.5",
44+
"firebase-tools": "^13.31.0",
4245
"pg": "^8.14.1",
4346
"tsx": "^4.6.2",
4447
"typescript": "^5.8.2",
@@ -50,7 +53,8 @@
5053
"db:generate": "drizzle-kit generate",
5154
"db:push": "tsx scripts/migrate.ts",
5255
"db:studio": "drizzle-kit studio",
53-
"dev": "docker compose up -d && vite dev",
56+
"dev": "docker compose up -d && firebase emulators:start --only firestore & vite dev",
57+
"dev:no-emulator": "docker compose up -d && vite dev",
5458
"lint": "eslint . --fix",
5559
"preview": "vite preview"
5660
},

examples/react/todo/src/lib/collections.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { createCollection } from "@tanstack/react-db"
22
import { electricCollectionOptions } from "@tanstack/electric-db-collection"
33
import { queryCollectionOptions } from "@tanstack/query-db-collection"
44
import { trailBaseCollectionOptions } from "@tanstack/trailbase-db-collection"
5+
import { firebaseCollectionOptions } from "@tanstack/firebase-db-collection"
56
import { QueryClient } from "@tanstack/query-core"
67
import { initClient } from "trailbase"
8+
import { initializeApp } from "firebase/app"
9+
import { connectFirestoreEmulator, getFirestore } from "firebase/firestore"
710
import { selectConfigSchema, selectTodoSchema } from "../db/validation"
811
import { api } from "./api"
912
import type { SelectConfig, SelectTodo } from "../db/validation"
@@ -14,6 +17,17 @@ const queryClient = new QueryClient()
1417
// Create a TrailBase client.
1518
const trailBaseClient = initClient(`http://localhost:4000`)
1619

20+
// Initialize Firebase
21+
const firebaseApp = initializeApp({
22+
projectId: `tanstack-db-demo`,
23+
})
24+
const firestore = getFirestore(firebaseApp)
25+
26+
// Connect to emulator in development
27+
if (import.meta.env.DEV) {
28+
connectFirestoreEmulator(firestore, `localhost`, 8080)
29+
}
30+
1731
// Electric Todo Collection
1832
export const electricTodoCollection = createCollection(
1933
electricCollectionOptions({
@@ -228,3 +242,39 @@ export const trailBaseConfigCollection = createCollection(
228242
},
229243
})
230244
)
245+
246+
// Firebase Todo Collection
247+
export const firebaseTodoCollection = createCollection(
248+
firebaseCollectionOptions({
249+
id: `todos`,
250+
firestore,
251+
collectionName: `todos`,
252+
getKey: (item) => item.id,
253+
schema: selectTodoSchema,
254+
rowUpdateMode: `partial`,
255+
parse: {
256+
created_at: (timestamp: any) =>
257+
timestamp?.toDate?.() || new Date(timestamp),
258+
updated_at: (timestamp: any) =>
259+
timestamp?.toDate?.() || new Date(timestamp),
260+
},
261+
})
262+
)
263+
264+
// Firebase Config Collection
265+
export const firebaseConfigCollection = createCollection(
266+
firebaseCollectionOptions({
267+
id: `config`,
268+
firestore,
269+
collectionName: `config`,
270+
getKey: (item) => item.id,
271+
schema: selectConfigSchema,
272+
rowUpdateMode: `partial`,
273+
parse: {
274+
created_at: (timestamp: any) =>
275+
timestamp?.toDate?.() || new Date(timestamp),
276+
updated_at: (timestamp: any) =>
277+
timestamp?.toDate?.() || new Date(timestamp),
278+
},
279+
})
280+
)

examples/react/todo/src/routeTree.gen.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { createServerRootRoute } from '@tanstack/react-start/server'
1313
import { Route as rootRouteImport } from './routes/__root'
1414
import { Route as TrailbaseRouteImport } from './routes/trailbase'
1515
import { Route as QueryRouteImport } from './routes/query'
16+
import { Route as FirebaseRouteImport } from './routes/firebase'
1617
import { Route as ElectricRouteImport } from './routes/electric'
1718
import { Route as IndexRouteImport } from './routes/index'
1819
import { ServerRoute as ApiTodosServerRouteImport } from './routes/api/todos'
@@ -32,6 +33,11 @@ const QueryRoute = QueryRouteImport.update({
3233
path: '/query',
3334
getParentRoute: () => rootRouteImport,
3435
} as any)
36+
const FirebaseRoute = FirebaseRouteImport.update({
37+
id: '/firebase',
38+
path: '/firebase',
39+
getParentRoute: () => rootRouteImport,
40+
} as any)
3541
const ElectricRoute = ElectricRouteImport.update({
3642
id: '/electric',
3743
path: '/electric',
@@ -66,33 +72,37 @@ const ApiConfigIdServerRoute = ApiConfigIdServerRouteImport.update({
6672
export interface FileRoutesByFullPath {
6773
'/': typeof IndexRoute
6874
'/electric': typeof ElectricRoute
75+
'/firebase': typeof FirebaseRoute
6976
'/query': typeof QueryRoute
7077
'/trailbase': typeof TrailbaseRoute
7178
}
7279
export interface FileRoutesByTo {
7380
'/': typeof IndexRoute
7481
'/electric': typeof ElectricRoute
82+
'/firebase': typeof FirebaseRoute
7583
'/query': typeof QueryRoute
7684
'/trailbase': typeof TrailbaseRoute
7785
}
7886
export interface FileRoutesById {
7987
__root__: typeof rootRouteImport
8088
'/': typeof IndexRoute
8189
'/electric': typeof ElectricRoute
90+
'/firebase': typeof FirebaseRoute
8291
'/query': typeof QueryRoute
8392
'/trailbase': typeof TrailbaseRoute
8493
}
8594
export interface FileRouteTypes {
8695
fileRoutesByFullPath: FileRoutesByFullPath
87-
fullPaths: '/' | '/electric' | '/query' | '/trailbase'
96+
fullPaths: '/' | '/electric' | '/firebase' | '/query' | '/trailbase'
8897
fileRoutesByTo: FileRoutesByTo
89-
to: '/' | '/electric' | '/query' | '/trailbase'
90-
id: '__root__' | '/' | '/electric' | '/query' | '/trailbase'
98+
to: '/' | '/electric' | '/firebase' | '/query' | '/trailbase'
99+
id: '__root__' | '/' | '/electric' | '/firebase' | '/query' | '/trailbase'
91100
fileRoutesById: FileRoutesById
92101
}
93102
export interface RootRouteChildren {
94103
IndexRoute: typeof IndexRoute
95104
ElectricRoute: typeof ElectricRoute
105+
FirebaseRoute: typeof FirebaseRoute
96106
QueryRoute: typeof QueryRoute
97107
TrailbaseRoute: typeof TrailbaseRoute
98108
}
@@ -149,6 +159,13 @@ declare module '@tanstack/react-router' {
149159
preLoaderRoute: typeof QueryRouteImport
150160
parentRoute: typeof rootRouteImport
151161
}
162+
'/firebase': {
163+
id: '/firebase'
164+
path: '/firebase'
165+
fullPath: '/firebase'
166+
preLoaderRoute: typeof FirebaseRouteImport
167+
parentRoute: typeof rootRouteImport
168+
}
152169
'/electric': {
153170
id: '/electric'
154171
path: '/electric'
@@ -225,6 +242,7 @@ const ApiTodosServerRouteWithChildren = ApiTodosServerRoute._addFileChildren(
225242
const rootRouteChildren: RootRouteChildren = {
226243
IndexRoute: IndexRoute,
227244
ElectricRoute: ElectricRoute,
245+
FirebaseRoute: FirebaseRoute,
228246
QueryRoute: QueryRoute,
229247
TrailbaseRoute: TrailbaseRoute,
230248
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createFileRoute } from "@tanstack/react-router"
2+
import { TodoApp } from "../components/TodoApp"
3+
import {
4+
firebaseTodoCollection,
5+
firebaseConfigCollection,
6+
} from "../lib/collections"
7+
8+
export const Route = createFileRoute("/firebase")({
9+
component: Firebase,
10+
})
11+
12+
function Firebase() {
13+
return (
14+
<TodoApp
15+
todoCollection={firebaseTodoCollection}
16+
configCollection={firebaseConfigCollection}
17+
type="Firebase"
18+
/>
19+
)
20+
}

examples/react/todo/src/routes/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ function HomePage() {
4242
</div>
4343
</button>
4444
</Link>
45+
<Link to="/firebase" className="block w-full">
46+
<button className="w-full px-6 py-4 bg-orange-500 text-white rounded-lg hover:bg-orange-600 transition-colors text-left">
47+
<div className="font-semibold">Firebase Collections</div>
48+
<div className="text-sm opacity-90 mt-1">
49+
Real-time sync with Firestore
50+
</div>
51+
</button>
52+
</Link>
4553
</div>
4654

4755
<div className="mt-8 text-xs text-center text-gray-500">

0 commit comments

Comments
 (0)