简体   繁体   中英

How to request data from Firestore defensively with Flutter

Since Firestore is a NoSQL database and has no strict type rules and defined document structures, I think about handling corrupt data in my Flutter app.

In case you wonder why I want to request defensively, even when it is no third-party API -> I can think of three reasons why my app crashes because of corrupt data:

  • I add wrong data via the Firebase console like using type string for a field that should have been type number (happened repeatedly).
  • A bug in my app adds corrupt data to Firestore.
  • A user has an old version of my app installed which can not handle the new Firestore data structure.

My requirements: The app should not crash when corrupt data from Firestore is requested, but the corrupt data should be reported so that the data can be fixed in Firestore asap

What do you think about the following approach?

Assume we have a model Movie .

Movie {
  final String title;
  final int releaseYear;

  Movie({required this.title, required this.releaseYear});

  Movie.from(Map<String, dynamic> data)
      : title = data['title'],
        releaseYear = data['release_year'];
}

The named constructor from parses the document data from DocumentSnapshot.data() and returns our model. This works fine as long as the data has a field title of type String and a field release_year of type int ( number in Firestore).

Let's assume the field release_year is missing in the actual data. This will let the request crash. Thus, the current user can't do anything with the respective movie and I as developer won't notice, because it happened silently on the device of the user.

To fix the first issue, we can use defensive parsing with fallback data like this: data['release_year']?? -1 data['release_year']?? -1 . No crash happens, but I as developer still don't notice and can't fix the data.

To fix also this issue we could use Firebase Crashlytics. The only problem is that if we use defensive parsing to prevent crashing no log will be sent to Firebase. That's why I came up with this solution:

final snapshot = await FirebaseFirestore.instance.collection('movies').doc('123').get();
try {
  return Movie.from(snapshot.data()!);
} catch (e) {
  await FirebaseCrashlytics.instance
      .recordError(e, e.stackTrace(), reason: 'data of movie ${snapshot.id} is corrupt');
  return Movie.fromCorrupt(snapshot.data()!);
}

First, the app tries to parse the document data without any fallback mechanism. If the data is corrupt an exception is thrown and catched. In the catch block, the error is send to Firebase and then the defensive parsing constructor fromCorrupt is called to let the user continue in the app with the remaining data. In fromCorrupt each field is checked on null and type, before it is used to create the model Movie . If a value is null or of a wrong type, a fallback value is used.

What do you think of my approach? Am I overengineering?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM