Hacker News new | ask | show | jobs
by TekMol 1465 days ago
According to the article, this is how you load messages and their users from a DB via Firebase:

    const querySnapshot = await getDocs(collection(db, "messages"));
    const userSnapshots = await Promise.all(
        querySnapshot.docs().map(async messageSnapshot => {
            return await getDoc(docSnapshot.data().creator);
            })
    );
Phew!

Thanks, but no. Never. I will keep doing it server side:

    $messages = DB::select(
        'SELECT * FROM messages JOIN users ON users.id=messages.user_id'
    );
It is amazing with how much cruft developers are willing to deal with these days. And how much CPU cycles get burned for nothing, as the Firebase example fires one query per message to get the user. This would be bad enough on the server. But with the Firebase example, it would also create a client-server http roundtrip for each message. Mind-boggling.
8 comments

Technically that's how you load data from Firestore specifically, but yes, that's how it's done. I started working with a team who uses Firestore, and let me tell you, I've never hated a database so much. No disrespect to anyone from the Google team, but I cannot fathom why they made the decisions they made.

1) You are severely limited in how you can query. The list of limitations is too long to recount here, but querying is nearly worthless.

2) The database design strongly pushes you towards nesting collections, making side effects cleanup a disaster, especially as a database grows in complexity.

3) You cannot sort on a field without creating an index first. I get why creating an index is a good idea, but I can't even write a simple analytics script without indexing the fields first.

4) It gears itself towards frontend developers who don't know how to write a backend, and encourages bad practices for them. One example: Firebase lets you manually edit production data very easily from within their dashboard. Like it's treated almost like it's a CSV.

5) The recommended development approach is to directly query the DB from the frontend, with no server in between. This means any data security has to be implemented in a separate DB rules document. The syntax and structure of this doc is super limited and often results in a giant, unmaintainable file.

6) Firestore cannot count. As in, you literally cannot query the number of records in a collection. If you want that value, you have to store it as a separate field in the collection, and then update that value each time you add and remove a doc. MADNESS.

I could go on, and on, and on.

Yes, these are all limitations. But the advantage that Firestore has is significant, and for some applications it's worth accepting those limitations:

* Firestore is truly a "fire and forget" datatabase that scales without effort or maintenance. If your app works for 500 users, it will work for 500 million. Without a devops staff.

Yes, Firestore (aka Cloud Datastore) feels crippled compared to running aggregations and joins on an RDBMS. If your data and load fit on a single node Postgres, by all means use it, that's a great solution! When your requirements exceed that, you're in a different world. You can look at Spanner ($$$) or its clones (operational load, maturity). Or you can do what I did, and run the firestore/datastore as a master database and replicate data to other stores (eg BigQuery) for analytics.

Firestore's sweet spots are very small (eg, you have many microservices and want simple cheap zero-maintenance persistence) or very large (where scaling and availability would give you headaches anyway). In the middle, traditional RDBMSes are great.

I use Firestore. I think your analysis is spot on. It is great for OLTP workloads. For query/analysis, you'll need to replicate to another database.

another area where it truly shines is zero downtime for schema updates.

I also just joined a team using Firestore and I feel exactly this same way. It absolutely blew me away how bad Firestore is for any kind of conventional CRUD app use cases. I have never worked with a database less fit for purpose, and I worked with MongoDB in 2012.
Yup. One of our products was built on Firestore and I expect 90% of my time on that project is spent trying to turn Firestore into a more featureful product to meet the application's requirements. You can push it pretty far if you have the developer resources, but it often seems ludicrous that someone is willing to spend that much to build what other database solutions provide out of the box for free.
Firestore makes it easy to get productive quickly and then drive straight into quicksand.
Couldn’t have said it better myself.
Same. I find myself wishing we were on Mongo, it's that bad.
> It gears itself towards frontend developers who don't know how to write a backend, and encourages bad practices for them. One example: Firebase lets you manually edit production data very easily from within their dashboard. Like it's treated almost like it's a CSV.

This is truly spot on. I'm one of those devs who couldn't write a good backend when I first began coding out apps (as a hobby). Started out with firebase because that was the tool used by most YouTube videos and Medium blog posts introducing people to app development. In the end, I eventually had to learn other technologies such as using Elasticsearch, DynamoDB (which I would also stay the hell away from) and PostGRES.

Thankfully Supabase decided to stay away from the NoSQL format, so learning how to get started with PostGRES was made smoother.

Looking back I don't even know why I jumped into using Firebase, since I do have a pretty good footing in SQL querying. I don't know why Google isn't bothering with an Elasticsearch-like NoSQL solution for Firebase. And quite frankly, I would only use Firebase for Auth and RTDB for basic database stuff. If I had known this earlier, I might have saved months of learning Firebase Cloud Firestore crap online.

It suffers from trying to go upmarket with something that was never meant for ultra-serious, large, production apps. Look at the original Wired article [1]: every story of the founders' pitching to customers is about being able to quickly zero-to-one a concept. It seems great for internal apps, quick prototypes, and simple things and that's about it.
Firestore has better SLO than Confluent's own managed Kafka offering (5 nines [1] vs 4 nines [2]).

If you use it as a system component and not a do everything backend it is the most production ready clientside technologies on the planet.

[1] https://docs.confluent.io/cloud/current/clusters/cluster-typ...

[2] https://cloud.google.com/firestore/sla

Yep. And what’s worse is that Firestore is notoriously difficult to migrate away from (at least from what I’ve heard, haven’t yet tried it myself). So if you take the prototype and turn it into an mvp, you’re kinda stuck with it.

If the goal is to create a prototyping DB, ideally there’d be some nice off ramping or migration tools for when the app needs to become production ready.

Great list! I think we've had similar frustrations with firestore.

#3 is not really correct. You can sort on any single field but if you have multiple fields, then yes, you must create an index.

#4 I partially agree. The web dashboard makes things I don't want to do (accidentally edit or delete a field/document) dangerously easy, and things I do want to do (copy the contents of a document, save the results of a query, copy the text of a field) exceedingly difficult. The truth is that firestore is geared toward people who want an easy way to get near real-time data synchronization. It really sacrifices almost everything else.

The number one most annoying thing to do with firestore is work with their security rules.

>1) You are severely limited in how you can query. The list of limitations is too long to recount here, but querying is nearly worthless.

that is probably a business decision. Google has api limites, so figures.

This has nothing to do with business. It's fundamental limitations from the technical architecture.

It does provide a lot of serverless scalability for what it offers, but it's the classic case of optimizing for a situation that won't happen for 99% of their apps.

That's a good guess, but no this is a syntax limitation. For instance, if you wanted to say "I want records where record.score >= 10 and record.score <= 100", you can't do it, because you can't filter on the same field twice.
This is not correct. You can filter on the same field twice. What you can't do is filter on different fields, eg:

I want records where record.score >= 10 and record.date <= 2022-01-01

The first form (single field) is a simple range query on a single index - that's what Firestore is optimized for. The second form (with different fields) potentially requires walking the near-entirety of both indexes looking for matches, and therefore has unbounded time and computational requirements.

Firestore is designed so that you can't do things that don't scale. Sometimes that sucks, especially when you know that the data volume for that query will always be "reasonable". But the limitations are not arbitrary.

I never assumed they were arbitrary, it's more that they don't fit the product. This DB architecture fits some use cases, I'm sure, but the decision should be made by an experienced person. In the real world, what happens is that early stage startups get hooked on the whole "fast and scalable prototype" marketing copy, then hire some FE devs that build the entire app on Firestore because they don't know any better. 99 times out of 100, a startup's use case is not going to be a good fit for Firestore, but Firebase has no vested interest in informing the customer about this little detail. I've only worked with Firebase for 1 year and I've now seen this exact situation happen 3-5 times.
A few points here:

1) That’s a JOIN and Firestore is not unique among NoSQL databases for being bad at it.

2) That’s the code you’d use in a web browser to load Firestore data. That code also handles all of the API and Authentication code you’d normally put in front of another DB. It’s serverless. So that is a fully functional snippet, your SQL example needs an API layer and frontend deserialization code.

3) It does not make a new HTTP connection per request. It uses a long-lived gRPC connection.

2: The code does not handle authentification. The code literally just says "Give me this, give me that...".

3: It is still a "a client-server http roundtrip" for every query. It does not open and close the http connection every time. But it sends a "GET / HTTP ..." request for every query. With hostname, accepted formats, encodings etc.

The code does not handle authentication because it does not need to - all data access and auth rules happen within Firestore/Firebase. If a client requests data they do not have access to, they won’t get it.
...and you will show your users stale data, which is a non-starter for a chat app. You will then need to implement some custom push/invalidate mechanism, based on a custom interpretation of the DB event log.

The entire point of the Fire* style of database is that these trade-offs are not worth it in the long run, that few development teams have the skill and time to implement this themselves, and that databases can and should solve this for you.

I have little love for Cloud Firestore, it's a trash fire riddled with poor decisions, but if you don't even understand what the problem is with that SQL query, you don't understand the expectations users have nowadays of front-end applications.

You're on HN, where we still long for the "documents with links" days of yore.
Why would a SQL query show stale data? Inserts and selects are fine, the only thing needed is signaling. You can use Firestore for that, or just a separate thin layer on a much more capable database backend.

> "that few development teams have the skill"

This is the fundamental problem. There's no magic answer to a lack of skill.

Because the way that you will ultimately have to scale something like PostgreSQL for example is going to end up with eventual consistency which Firestore doesn’t have to deal with.
There's no reason for eventual consistency. Firestore is backed by BigTable and Spanner (both strongly consistent just like Postgresql). There's nothing magic about this - it just provide a pub/sub channel on the watched keys and automatically does a SELECT loop in the background once an INSERT triggers that key.

You can do the same thing on a simple by selecting by a `chatid` in a table that'll get the latest messages/inserts. Again, the only thing needed is the pub/sub layer, not an entirely different database.

I mean if it's brevity you're after, the first example can be rewritten in a terser style:

    const userSnapshots = await Promise.all(
        getDocs(collection(db, "messages")).then(
            docs => docs().map(snapshot => getDoc(snapshot).data().creator)
        )
    )
Ultimately you're kinda just comparing preference for SQL syntax over JS in that case.

The JS example is also async (always adds visual complexity to code calls), whereas the PHP example is blocking. That SQL query doesn't look particularly efficient either...

There's plenty of good solid criticisms of Firebase & NoSQL but these aren't it. e.g. your HTTP roundtrip argument has been debunked by sibling commenters, but client-side DB calls is still riddled with adjacent problems & ultimately adding more complexity to the server-side avoid the need for client-side DB access is often worthwhile.

Most apps I build have a rdbms main data store but I use firestore for things like real time interactions or more recently real time commenting.

It would be a bit more time consuming and challenging to set this up across iOS, android, and web. A lot more time would be spent figuring out the system to make this work well and without issues at scale. On the other hand… It takes like 20 min to set up a real time feature with firestore that works across all platforms. It scales well and works great for what it is.

If people try to use it in place of a rdbms or complex use cases, they’re going to have a bad time. But for those who can utilize their tool belt effectively, it’s awesome.

I love firebase products.

Check out Supabase, you can have your cake and eat it too.
Neat. The rpc to Postgres seems really cool. How does it scale compared to firebase (do you have to think too much or just implement and move on)?
You can in fact create a server side query in Firebase if you wanted.

The fact that Firebase isn't using SQL is obvious and comes with up and downsides.

I definitely would recomend keeping it server side if you don't need any other firebase features. You don't use firebase as a normal database. You use it for its realtime featureset.
This is a strawman and no one would actually design a messaging app using a NoSQL db like that. Typically one would employ a strategy to fan-out data such that the message document has everything needed to render a message in the UI.