{"id":15490589,"url":"https://github.com/blaugold/firebase-rxjs","last_synced_at":"2025-04-22T19:11:00.289Z","repository":{"id":57236667,"uuid":"86557516","full_name":"blaugold/firebase-rxjs","owner":"blaugold","description":"Firebase with Observables, Type Checking of Schema, Zone.js aware and Angular ready.","archived":false,"fork":false,"pushed_at":"2017-09-01T22:38:47.000Z","size":825,"stargazers_count":17,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-19T09:18:55.088Z","etag":null,"topics":["angular","firebase","rxjs","static-typing","typescript","zone"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blaugold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-29T08:32:27.000Z","updated_at":"2022-03-11T11:24:11.000Z","dependencies_parsed_at":"2022-08-26T15:11:29.964Z","dependency_job_id":null,"html_url":"https://github.com/blaugold/firebase-rxjs","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blaugold%2Ffirebase-rxjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blaugold%2Ffirebase-rxjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blaugold%2Ffirebase-rxjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blaugold%2Ffirebase-rxjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blaugold","download_url":"https://codeload.github.com/blaugold/firebase-rxjs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250306638,"owners_count":21408926,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","firebase","rxjs","static-typing","typescript","zone"],"created_at":"2024-10-02T07:22:28.154Z","updated_at":"2025-04-22T19:11:00.264Z","avatar_url":"https://github.com/blaugold.png","language":"TypeScript","readme":"# FirebaseRxJS\n\nFirebase with Observables, Type Checking of Schema, Zone.js aware and Angular ready.\n \n- Asynchronous results are returned as observables.\n- Strings in `.child(\"pathSegment\")` can be validated through static type checking and a schema interface.\n- If zone.js is in scope firebase sdk runs in separate zone.\n- For use in angular apps `FirebaseRxJSModule` is provided.\n\nTypeScript@2.1.0 and angular@4.0.0 are required.\n\nTypeScript Docs:\n\n- [firebase-rxjs](https://blaugold.github.io/firebase-rxjs/firebase-rxjs/)\n- [firebase-rxjs-angular](https://blaugold.github.io/firebase-rxjs/firebase-rxjs-angular/)\n\n```bash\nnpm install --save firebase-rxjs\n# For angular module\nnpm install --save firebase-rxjs-angular \n```\n\n## Configuration\n\nCreate a new `FirebaseApp` by passing in the config from [Firebase Console](https://console.firebase.google.com/).\n\n```typescript\nimport { FirebaseApp } from 'firebase-rxjs'\n\nconst app = new FirebaseApp({ options: {\n    apiKey: \"...\",\n    authDomain: \"...\",\n    databaseURL: \"...\",\n    storageBucket: \"...\",\n    messagingSenderId: \"...\"\n}});\n\n// Get a reference to `FirebaseAuth`\nconst auth = app.auth()\n\nauth.createUserWithEmailAndPassword(\"...\", \"...\").subscribe(user =\u003e {\n  ...\n})\n```\n\n## Usage\n\nTo get static type checking when accessing the database define a database schema interface.\nIts best to compose the schema out of smaller interfaces. This keeps your schema clear and\nallows you to easily access subsections with `db.ref\u003cSubSchema\u003e('path/to/something')`.\n\n```typescript\n// database-schema.ts\nexport interface UserProfile {\n  firstName: string\n  lastName: string\n  twitter: string\n  profileImg: string\n}\n\nexport interface Comment {\n  text: string\n  createdAt: number\n  author: string\n  authorId: string\n}\n\nexport interface Comments {\n  [commentId: string]: Comment\n}\n\nexport interface BlogPost {\n  title: string\n  summary: string\n  body: string\n  author: string\n  authorId: string\n  publishedAt: number\n  updatedAt: number\n  comments: Comments\n}\n\nexport interface DBSchema {\n  users: {\n    [userId: string]: UserProfile\n  }\n  blogPosts: {\n    [postId: string]: BlogPost\n  }\n}\n```\n\nNow when getting a reference of the database use the schema interface as the type parameter.\n\n```typescript\nimport { FirebaseApp } from 'firebase-rxjs'\nimport { DBSchema } from './database-schema.ts'\n\nconst app: FirebaseApp\nconst db = app.database\u003cDBSchema\u003e()\n\n// Now the compiler will yell at you if you use the wrong path segments.\ndb.ref().child('userz')\n\n// Or if you go deeper in the tree pass a sub schema as the type parameter.\nconst commentsRef = db.ref\u003cComments\u003e('blogPosts/1/comments')\n\n// Using a schema will also give you better IntelliSense when accessing the retrieved data.\ncommentsRef.onValue().list().subscribe(comments  =\u003e {\n  comments[0].key\n  comments[0].val.body\n})\n```\n\nThe result of onValue, onChildAdded, etc is a `DataSnapshotObservable\u003cT\u003e` which is typed \naccording to the previous calls and the schema. It provides some helpful methods to make working\nwith `DataSnapshots` easier, but the snapshots can be accessed directly too. \n\nMost methods overall mirror the firebase sdk and behave the same.\n\n## Angular\n\nInclude the `FirebaseRxJSModule` in your root module. It is possible to provide multiple apps for\ninjection but only one primary app. The primary `FirebaseApp` will make itself, `FirebaseAuth` and\n`FirebaseDatabase` injectable directly. Secondary apps have to be configured with an \n`InjectionToken` which will inject the `FirebaseApp`. \n\n```typescript\nimport { NgModule, InjectionToken } from '@angular/core'\nimport { FirebaseRxJSModule, FirebaseApp } from 'firebase-rxjs-angular'\n\nconst secondaryApp = new InjectionToken\u003cFirebaseApp\u003e()\n\n@NgModule({\n  ...\n  imports: [\n    FirebaseRxJSModule.primaryApp({options: ... }),\n    FirebaseRxJSModule.secondaryApp(secondaryApp, {options: ... }),\n  ],\n  ...\n})\nexport class AppModule {}\n```\n\n## Zone.js\n\nIf `Zone` is globally available all calls to the firebase sdk will run in an isolated zone, forked \nfrom the root zone. Alternatively a zone can be passed to the `FirebaseApp` constructor. All other \nmethods take the zone of their call site and schedule tasks with it. \n\nMotivation for this feature is a problem which lets all protractor tests timeout unless synchronization is \nturned off. The cause lies in how protractor determines when changes stemming from the last input \nhave propagated and the next instruction or expectation can be executed. It does this by observing\nwhether or not MicroTasks and MacroTasks are scheduled to run. Once the queues of theses tasks are \nempty it's safe to assume the dom wont change. EventTasks like listeners on `WebSocket.onmessage` \ncould run and change the dom but that is unpredictable and can not be factored in to the decision\nwhen to proceed with the test. Now what if micro and macro tasks schedule new tasks themselves? \nProtractor again will wait until all tasks have run. And this is what is problematic. When using \nthe database the firebase sdk sends a heartbeat by setting up a timeout which first sends the \nheartbeat and then reschedules the next timeout. The result is a recursive chain of never ending \nMacroTasks keeping the task queue dirty. Independently of that the firebase sdk sets up a long \nrunning (around 30 seconds) timeout during initialization timing out any test by itself. \n\n## Real Time\n\nIts worth noting that all write operations and one off reads are scheduled as MacroTasks, \nmeaning protractor or angular's `async` test helper will wait until these tasks have run. This is not \ntrue for observables returned from `.{on}{Value,ChildAdded,ChildRemove,ChildChanged,ChildMoved}()`.\nThe semantics of MacroTasks are that they are\n\u003cblockquote\u003eguaranteed to execute at least once after some well understood delay.\u003c/blockquote\u003e\nFor a real time databases like Firebase the correct tasks to use is the EventTask. This type of task\nis expected to run zero or multiple times with unpredictable timing. So when using the methods \nstarting with `on`, tests relying on zone.js will not wait for them to complete, unless the framework\nis configured to wait for all EventTasks to be canceled. For each observable\nwhich emits real time database events the library schedules an EventTask on subscription and cancels \nit when the observable is unsubscribed. To make testing these kinds of observables simpler a test helper for\nthe jasmine testing framework is included:\n\n```typescript\nimport { asyncEvents } from 'firebase-rxjs'\n\ndescribe('Suite', () =\u003e {\n  it('should wait for EventTasks to clear', asyncEvents(async () =\u003e {\n    const db: FirebaseDatabase\u003cany\u003e\n    // This observable will unsubscribe itself after emitting 3 events.\n    db.ref().child('foo').onValue().take(3).subscribe()\n    \n    // Multiple observables or other asynchronous tasks will block the test until their \n    // resolution too.\n    const res = await fetch('https://www.google.com')\n  }))\n})\n```\n\nEnd-to-end tests should poll content on the page under test, which is dependent on real time changes,  \nuntil a expected state is reached or timeout. \n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblaugold%2Ffirebase-rxjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblaugold%2Ffirebase-rxjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblaugold%2Ffirebase-rxjs/lists"}