Firestore Roadmap
posva opened this issue ยท 37 comments
It's time for vuefire to update to Cloud Firestore ๐
In order to give you some visibility about the new version, here's a checklist with features. Feel free to ask about new things, I'll probably forget to add every single feature to the list.
Until the version becomes stable there may be breaking changes, but if you're using the current version of vuefire, don't worry, it won't affect you. The new version will be published under the next
tag on npm, meaning that you can already play with it if you want
Firestore is now supported by the latest tag
Keep in mind, there're still no docs but you can check the exapmles/
folder and tests for inspiration
Features
- Document binding
- Collection binding
- unbind on destroy
- Manual bind
- Manual unbind
- Support firestore as a function
- bind -> returns a promise
- option to remove data right away when binding on top of an already bound ref
- Automatic unbind when already bound
- Development warnings
- Recursive ref binding + unbinding
- Bind refs in documents
- bind refs inside of objects
- bind refs inside of arrays
- bind plain refs
- Stop at 1st depth by default
- allow passing max depth
- Bind refs in documents
- customizing
id
key used - Move tests to Jest
- Split code to have both, vuefire and vuexfire code here
- Allow users to use both RTDB and Firestore at the same time
- Documentation
- Vuepress site
- Live Examples
- Figure out a way to document common things between vuefire /vuexfire
- Vuexfire
- Good practices about binding documents and collections
- Where to host references to collections and others (#180)
- Cookbook
- Pagination
- How to write tests
- Using without a bundler (prototyping)
- Add full template to use
- Using with Typescript
- Dynamically bound properties (
Vue.set
)
- Note in getting started about docs being for people using webpack (or project with cli
- Link to cookbook
If you find bugs, please, open a new issue (but make sure to check if it hasn't been fixed already ๐ )
if you want to check the ongoing work, it's on the firestore branch
Small update: Things are working really well, I just released an alpha version. The reason is taking so much time is because there's a lot of edge cases for refs (embed documents into other documents) to handle ๐
Most things are working but if you find bugs, please open an issue with a repro, it would really help!
For instance, I yet have to fix a bug when updating a document referenced in an array on a bound document that makes the update delay fixed in latest release
Thanks for your hard work in supporting Firestore. When binding to a collection, is there a way to get the ID or ref of each document within the collection?
it's the key
id
attribute. It's non-enumerable, so it doesn't appear when copying the object
Thanks. I was using a collection with v-for on a component and binding to the object instead of binding on each property. This meant that the id prop in the component could not be found.
Specifying a property for the id did the trick:
<ListingItem v-for="listing in listings" :key="listing.id" v-bind="listing" :id="listing.id"/>
sorry, I meant id
๐
I have a concern with this:
If you are looking for Firebase realtime support, install v1 instead: npm i vuefire@v1
Does this mean VueFire is dropping support for Firebase RTDB? It's still functional and doesn't appear to be going away any time soon. Cloud Firestore isn't purely an upgrade to RTDB - it's a sidegrade with its own strengths and weaknesses. RTDB is great for getting JSON data easily synced between clients, and I don't want to see VueFire lose that featureset.
No, don't worry, the realtime db will still be supported and receive bug fixes and even feature requests in vuefire v1. I will probably spend more time working on v2 (Firestore) because it's funnier and you know, it's all on my free time ๐, but both versions will be supported for some time. That being said, there's not much to add to vuefire except for already opened issues and PR.
The readme of the Firestore branch is prepared for release, that's why it tells you to install vuefire@1
but you can still do vuefire
ok, something important... i can install vuefire@next AND USE FIREBASE ON IT? im making a new app that will be REALLY BIG, and im making it using vuefire, i want to use the lattest version so i can use firestore instead of firebase for better queries, so... that said, can i use vuefire@next to use both firebase and firestore?
It works like a charm ! Thanks for your job
@BernardMarieOnzo as noted in the first post:
Keep in mind, there're still no docs but you can check the exapmles/ folder and tests for inspiration
Here's a direct link to the firestore branch's examples
folder:
Vuefire: https://github.com/vuejs/vuefire/blob/master/packages/vuefire/examples/index.html
Vuexfire: https://github.com/vuejs/vuefire/blob/master/packages/vuexfire/examples/index.html
You might also find this medium article helpful as they walk through the end-to-end process of setting up and using Vue + VueFire + Firestore:
https://medium.com/vue-mastery/full-stack-vue-js-with-firestore-62e2fe2ec1f3
Edit: Also note that while the syntax and usage is similar to the RTDB approach you linked to, the syntax and usage is different - eg. it matches the firestore
library methods rather than the firebase
methods.
How would I go about "watching" a property in Firestore?
So my component has this:
firestore () {
return {
transcriptionsFirestoreRef: db.collection(this.id).doc('transcriptions')
}
}
How can I know when transcriptionsFirestoreRef
is updated?
@nick-jonas I guess you can go like this:
watch: { transcriptionsFirestoreRef: function (newVal, oldVal) { // code } }
@FlorianWerndl that's exactly it, wasn't sure if it would work with this but it seems to - thanks!
A quick one. Was wondering what the recommended pattern for catching the event when the $bind has taken place. There used to be a 'readyCallBack' function in the RTDB version, anyone have any suggestions?
@tukutela it's a promise now (see the issue content below)! I really need to write some docs for this ๐
yeah I see it, thanks.
Great work. Something that isn't clear, can we use Firebase and Firestore in v2, like so? :
firestore() {
return {
testcollection: firestore.collection('testcollection')
}
},
firebase() {
return {
testref: db.ref('testref')
}
},
I imagine people will either move over to Firestore gradually or else use both RTDB and Firestore for their projects - knowing whether the one library supports both would be great.
Moving to Firestore gradually looks really complex since you will have to mix both dbs collections and that can quickly become a pain.
It's not supported yet but I will probably make it possible in the future, but it's important to me not to impact people who are only using firestore
@posva If you've a large app with thousands of users and built with Vue-fire, but you want to leverage Firestore, it's not unlikely that you'll build the next set of features for the app on Firestore and then consider migrating more data over --- or not, as migrating all your data from RTDB to Firestore is definitely more complex than a few API differences across distinct components in your app -- plus both databases can be used for different use cases.
it's important to me not to impact people who are only using firestore
Sure, but most people who use vue-fire, do so because it works with the RTDB. Firestore is an additional, not a replacement product, so it would be strange if people were to call npm i vue-fire in the future and discover a library that no longer works with the RTDB.
I don't intend to come across as ungrateful - this library is excellent - I'm just wondering if you may be making assumptions that users don't share. Something worth considering.
I haven't been able to do more for a long time. The packages have been stable for some time, I need to write documentation add support for RTDB and then I will publish a stable release. I will get some time to work on this during xmas. Hopefully I should be able to publish it before 2019
I'm trying to use vuefire@next on a project, but I can't seem to be able to import the firePlugin export, as it seems it's not the correct export name as of your main.js. What's up with this? Am I doing something wrong here?
Also: do I need to import Firebase as a dependency too or is it already implemented in Vuefire?
@coffeeneed You need to import Firebase and follow their documentation to initialize the App. You are looking at the documentation for the next release. I shouldn't have push that to master yet. My bad. Check the repository at this commit: https://github.com/vuejs/vuefire/tree/4c8068ee0b043e60e554e1fc272709432f706983 The readme is in sync with the latest release @next
an update on this: Been really busy with work + personal life since January. I want to have at least the guide ready for both vuefire and vuexfire before releasing the version as stable. Especially because being able to support both RTDB and Firestore includes some breaking changes in exports to make the library tree shakable so people do not include code they don't need
I just published a new version with support for RTDB. There are a few breaking changes to accomodate that it's a matter of renaming imports https://twitter.com/posva/status/1109175186694328322
There are also wip docs at https://vuefire.netlify.com/ (not final link). Feedback welcome
Hi, I started to use vuexfire
I know there is an entry in the roadmap called How to write tests but these days I'm facing some difficulties to write unit tests to some modules with vuexfire
. If someone knows a place to read something about or see any examples would be great!
If I'll be able to write some tests I can definitely contribute with docs and examples.
@joelxr I think you could check how tests are written in this repository. We have our own mock for Firestore and we https://github.com/soumak77/firebase-mock for the RTDB but it should be able to mock both
Hey @posva - Passing in the last doc and the .startAt(lastDocFromQuery) isn't returning results.
The issue is that the last document isn't a firesore snapshop which is what is required for the SDK to work. If I get a few minutes I can take the Todo's sample and slap together a repo demonstrating the issue. Currently the only fix that I understand is to query the DB for a record and store it. Then passing it in on the bind.
const lastDocId = this.items[this.items.length - 1].id
lastItem = await db.collection('marketplace').doc(lastDocId).get()
// notice this isn't lastItem.data()
...
//then when we want to go to next page:
this.$store.dispatch('searchDatabase', originalQueryRef.startAt(lastItem).limit(n))
// where action:
searchDatabase: firestoreAction(async ({ bindFirestoreRef, commit }, ref) => {
// set state loading = true
commit('setToggleSearching', true)
await bindFirestoreRef('items', ref)
// set state loading = false
commit('setToggleSearching', false)
})
It would be great if vuexfire would hold on to these first and last document refs (hell they might be but I'm not seeing it). If these were to persist and be accessible somehow then folks wouldn't have to hit the database 3 times per page (after the first initial page, which would only have 2 DB calls). I'm 99.999% sure you have access to these first and last document refs within the @posva/vuefire-core
package.
By the way - thanks a million for the work on these packages, incredible work! My brain turns into a puddle of junk when I try to understand the code that goes into these wonderful tools.
[edit]
I was able to get pagination working with the bound items via:
in vuexfire.esm.js
function createSnapshot(doc) {
// defaults everything to false, so no need to set
// change from this
return Object.defineProperty(doc.data(), { 'id': { value: doc.id });
// to this
return Object.defineProperties(doc.data(), { 'id': { value: doc.id }, ['.ref']: { value: doc } });
}
Which the above probably could be done in a custom serialize
option ... but ...
in the function:
function extractRefs(doc, oldDoc, path, result) {
there this:
// TODO: this won't work if the user defines their own defined properties
// should we do it for every non enumerable property?
var idDescriptor = Object.getOwnPropertyDescriptor(doc, 'id');
if (idDescriptor && !idDescriptor.enumerable) {
// console.log('hi there extractRefs id: ', idDescriptor)
Object.defineProperty(data, 'id', idDescriptor);
}
Which I had to add another if check on:
idDescriptor = Object.getOwnPropertyDescriptor(doc, '.ref');
if (idDescriptor && !idDescriptor.enumerable) {
// console.log('hi there extractRefs id: ', idDescriptor)
Object.defineProperty(data, '.ref', idDescriptor);
}
So now when setting up vuex module:
searchDatabase: firestoreAction(async ({ bindFirestoreRef, commit }, queryRef) => {
// set state loading = true
commit('setToggleSearching', true)
await bindFirestoreRef('items', ref)
// set state loading = true
commit('setToggleSearching', false)
})
This now gives item['.ref']'s
that is passable to firebase firestore SDK so this will now work:
this.searchDatabase(queryRef.startAfter(lastItem['.ref']).limit(pageSize))
@joemanfoo Thanks, I'm glad you like the library! If you manage to reproduce it, please open an issue with the repro. You can use one of the examples as a starting point: https://github.com/vuejs/vuefire/tree/master/packages/vuefire/examples
Dear @posva,
Thank you for creating and sharing this awesome project. I would like to use both the RTDB and Firestore in the same project. Is it just that the documentation around this is not yet available or is it currently hard to implement? Is there any chance you could provide a gist or point me in the right direction for implementing this?
Thanks!
Sebastiaan
@sebastiaan-de-vries you can use both, you need to follow both examples and use both plugins to support firebase
and firestore
options. For Vuex, you can also do firebaseAction(firestoreAction(...))
@joemanfoo Vuexfire 3.2.1 seems to have fixed the issue with non-enumerable properties being removed from the modified DocumentData
(through the serialize
option).
By the way, I was also looking into a way to get the last document's reference to get pagination done with a cursor by using the .startAt()
, and the exposed workaround works perfectly, thanks! I wish there was an easier way to get the first and last document references though.
If I could make a suggestion for the cookbook...since I have struggled with this issue for a hot minute now; but I think I've got a workaround that kinda works. An example for Firebase Hosting that uses the reserved URL configuration would be nice. I've come up with the following code for my Firebase initialization (it's kind of simple because I don't use the GeoPoints or Timestamps):
EDIT: Nevermind my code, it doesn't work...apparently Webpack (any by extension the Vue CLI service) doesn't allow mixing module types; so, I can't have an ES module with AMD or CJS exports.
EDIT 2: I finally came up with a working solution. It's not the most elegant, but I think it would really help people come up with a working solution for a production application.
src/plugins/firebase/firebase.js
import axios from 'axios';
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
// Initialize the Firebase SDK
// NOTE: Since we are using Firebase Hosting, we use the "reserved" URL
// configuration. Therefore, instead of returning the firebase instance,
// we return a Promise that returns the firebase instance.
export default async function initializeFirebase() {
return new Promise((resolve, reject) => {
axios.get('/__/firebase/init.json').then(async (res) => {
try {
firebase.app();
resolve(firebase);
} catch (err) {
firebase.initializeApp(await res.data);
resolve(firebase);
}
}).catch((err) => {
reject(err);
});
});
}
src/plugins/firebase/init.js
import initializeFirebase from './firebase';
(async () => {
await initializeFirebase();
})();
src/plugins/firebase/db.js
(could also apply to auth, functions, etc)
This one seems unneccessay, but I did it this way, so that I can go back and expand it later; in case, I need to export Timestamps
, GeoPoints
, etc.
/* eslint-disable import/prefer-default-export */
import firebase from 'firebase/app';
import 'firebase/firestore';
export const db = firebase.firestore();
src/plugins/firebase/index.js
import { auth } from './auth';
import { db } from './db';
import './init';
export { auth, db };
src/main.js
import Vue from 'vue';
import { firestorePlugin } from 'vuefire';
// Other imports
import App from './App.vue';
import router from './router';
import store from './store';
import './plugins/firebase/init';
Vue.config.productionTip = false;
Vue.use(firestorePlugin);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
I did do the init in two places, which isn't an issue since I'm just initializing the Firebase SDK and my code already looks to see if there is a default app, but I really wanted to make sure it's initialized before it's needed.
Really looking forward to the Pagination Cookbook...
Any chance you could just add a non-enumerable doc
property (same as id
) to the data values that get bound or otherwise return the doc
or docs
via bindFirestoreRef()
in some way? I'm hitting the same issue needed to use Firestore cursors using startAfter(doc)
but don't see a solution other than reloading the document.
UPDATE: OK ... I think I found the answer which I've just documented on StackOverflow. I see its in this RoadMap. I saw that serializer
was referenced above but was "clear as mud" what it all meant so I've documented it here:
Any solution to pagination?
Really looking forward to the Pagination Cookbook...
Well, hello there