คำถามสัมภาษณ์ Firebase 2026: การยืนยันตัวตนใน Flutter, Firestore และกฎความปลอดภัย
คำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutter พร้อมคำตอบเป็นโค้ด Dart: สตรีมสถานะการยืนยันตัวตน, Google Sign-In v7, CRUD และการออกแบบข้อมูลใน Firestore, กฎความปลอดภัย, custom claims, transaction และการเปลี่ยนแปลงใน FlutterFire 5

คำถามสัมภาษณ์ Firebase สำหรับตำแหน่ง Flutter มักวนอยู่กับห้าหัวข้อ ได้แก่ สตรีมสถานะการยืนยันตัวตนทั้งสามตัว การสร้างแบบจำลองข้อมูลใน Firestore กฎความปลอดภัย การเขียนขณะออฟไลน์ และโมเดลค่าใช้จ่ายเบื้องหลังการอ่านแต่ละครั้ง Firebase ยังคงเป็นแบ็กเอนด์มาตรฐานของแอป Flutter ในปี 2026 และตั้งแต่เดือนตุลาคม 2026 ปลั๊กอิน FlutterFire ออกเป็น BoM 5.x (firebase_core 5.1.0, firebase_auth 7.0.1, cloud_firestore 7.1.0) โดยยกระดับ minSdk ของ Android เป็น 24 และใช้ Firebase Apple SDK 13 อยู่เบื้องหลัง
FlutterFire รุ่นปัจจุบันคือ BoM 5.1.0: firebase_auth 7.0.1, cloud_firestore 7.1.0, firebase_core 5.1.0 เวอร์ชันหลัก 5.0.0 กำหนดให้ใช้ minSdk ของ Android 24 สร้างบน Firebase Apple SDK 13.0.0 และเปลี่ยนชื่อพารามิเตอร์ของ activate ใน App Check ส่วน Firestore Pipelines ที่เพิ่มเข้ามาใน cloud_firestore 6.3.0 ได้รับ expression ใหม่เพิ่มขึ้นในทุกรุ่น
คำถามสัมภาษณ์ Firebase: สิ่งที่ถูกถามในปี 2026
ผู้สัมภาษณ์แทบไม่ถามรายละเอียด API แบบท่องจำ แต่จะตรวจสอบว่าผู้สมัครเข้าใจหรือไม่ว่า Firebase บังคับใช้กฎที่ใด แต่ละการดำเนินการมีค่าใช้จ่ายเท่าไร และเกิดอะไรขึ้นเมื่อเครือข่ายหลุด ตารางด้านล่างจับคู่หัวข้อที่พบซ้ำบ่อยกับสิ่งที่คำตอบที่ดีต้องมี ทุกหัวข้อมีคำตอบโดยละเอียดในส่วนคำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutterด้านล่าง
| หัวข้อ | คำถามที่พบบ่อย | สิ่งที่คำตอบที่ดีต้องมี |
|---|---|---|
| สตรีม auth | authStateChanges() vs idTokenChanges() vs userChanges() | แต่ละตัวส่งเหตุการณ์ใด และตัวไหนใช้ป้องกันการนำทาง |
| ความปลอดภัย | การควบคุมสิทธิ์บังคับใช้ที่ใด? | กฎความปลอดภัยบนเซิร์ฟเวอร์ ไม่ใช่การตรวจสอบฝั่งไคลเอนต์ ส่วน App Check เป็นอีกชั้นหนึ่ง |
| บทบาท | จะสร้างบทบาท admin อย่างไร? | Custom claims ที่ตั้งค่าผ่าน Admin SDK และอ่านผ่าน request.auth.token |
| แบบจำลองข้อมูล | ออกแบบข้อมูลสำหรับแอปแชท | Subcollection, denormalize ข้อความล่าสุด, หนึ่ง document ต่อหนึ่งข้อความ |
| ความสอดคล้อง | Transaction หรือ batched write? | Transaction อ่านแล้วเขียนและลองใหม่ได้ ส่วน batch เขียนอย่างเดียวแบบ atomic |
| ออฟไลน์ | การเขียนขณะออฟไลน์จะเป็นอย่างไร? | แคชภายในเครื่อง, hasPendingWrites, ซิงค์เมื่อกลับมาออนไลน์ |
| ค่าใช้จ่าย | ทำไมหน้าจอนี้แพง? | คิดค่าบริการต่อการอ่าน document, listener, การแบ่งหน้าด้วย cursor |
| การเปรียบเทียบ | Firestore vs Realtime Database | รูปแบบการ query, การขยายขนาด, หน่วยการคิดค่าบริการ |
การตั้งค่า Firebase ในโปรเจกต์ Flutter
ก่อนเขียนโค้ด Firebase ใด ๆ โปรเจกต์จำเป็นต้องได้รับการกำหนดค่าเริ่มต้นอย่างถูกต้อง FlutterFire CLI จะทำการลงทะเบียนแพลตฟอร์มโดยอัตโนมัติและสร้างไฟล์การกำหนดค่าสำหรับ Android, iOS, เว็บ และ macOS
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase with platform-specific config
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}ไฟล์ firebase_options.dart ถูกสร้างขึ้นโดยอัตโนมัติเมื่อรัน flutterfire configure ไฟล์นี้มีคีย์ API, รหัสโปรเจกต์ และตัวระบุเฉพาะแพลตฟอร์ม จึงไม่ต้องคัดลอกสิ่งใดด้วยตนเองจาก Firebase console
Firebase Authentication: อีเมล, Google และหลายปัจจัย
Firebase Authentication จัดการตัวตนของผู้ใช้ด้วยโค้ดสำเร็จรูปที่น้อยที่สุด แพ็กเกจ firebase_auth รองรับอีเมล/รหัสผ่าน, ผู้ให้บริการ OAuth (Google, Apple, GitHub), การยืนยันผ่าน SMS ทางโทรศัพท์ และการยืนยันตัวตนแบบหลายปัจจัย รวมถึง TOTP บน macOS ตั้งแต่ firebase_auth 6.1.0
การลงทะเบียนด้วยอีเมลและรหัสผ่าน
ขั้นตอนที่พบบ่อยที่สุดเริ่มต้นด้วยอีเมล/รหัสผ่าน เมธอด createUserWithEmailAndPassword จะคืนค่า UserCredential ที่มีอ็อบเจกต์ User ที่ยืนยันตัวตนแล้ว
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// Register with email and password
Future<User?> register(String email, String password) async {
try {
final credential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return credential.user;
} on FirebaseAuthException catch (e) {
// Handle specific error codes
switch (e.code) {
case 'email-already-in-use':
throw Exception('This email is already registered');
case 'weak-password':
throw Exception('Password must be at least 6 characters');
default:
throw Exception('Registration failed: ${e.message}');
}
}
}
// Sign in with existing credentials
Future<User?> signIn(String email, String password) async {
final credential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return credential.user;
}
// Reactive auth state stream
Stream<User?> get authStateChanges => _auth.authStateChanges();
}สตรีม authStateChanges() เป็นวิธีที่แนะนำในการติดตามสถานะการเข้าสู่ระบบทั่วทั้งแอป โดยจะส่งค่า User ปัจจุบันเมื่อมีการสมัครรับข้อมูล และทำงานอีกครั้งในทุกเหตุการณ์การเข้าสู่ระบบหรือออกจากระบบ
กฎของรหัสผ่านไม่จำเป็นต้องอยู่ฝั่งไคลเอนต์เพียงอย่างเดียวอีกต่อไป API validatePassword (มีตั้งแต่ firebase_auth 6.0.0) ตรวจสอบรหัสผ่านเทียบกับนโยบายที่กำหนดไว้ใน Firebase console ซึ่งสามารถกำหนดความยาวขั้นต่ำ ตัวพิมพ์ใหญ่ ตัวพิมพ์เล็ก ตัวเลข และอักขระพิเศษได้
การผสานรวม Google Sign-In
ผู้ให้บริการ OAuth ต้องใช้แพ็กเกจเพิ่มเติม ตั้งแต่ google_sign_in 7.0 ปลั๊กอินนี้เป็น singleton ที่เรียกผ่าน GoogleSignIn.instance ต้อง initialize เพียงครั้งเดียวเท่านั้น และแยกการยืนยันตัวตนออกจากการอนุญาตสิทธิ์ การเรียก GoogleSignIn().signIn() ที่พบในบทเรียนเก่าจะคอมไพล์ไม่ผ่านอีกต่อไป
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
// Call once at startup, before any other GoogleSignIn method
Future<void> initGoogleSignIn() => GoogleSignIn.instance.initialize();
Future<UserCredential> signInWithGoogle() async {
try {
// Shows the native account picker; throws if the user cancels
final googleUser = await GoogleSignIn.instance.authenticate();
// Synchronous getter since v7: only the ID token is exposed here
final googleAuth = googleUser.authentication;
// Firebase only needs the ID token to create the credential
final credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
);
return FirebaseAuth.instance.signInWithCredential(credential);
} on GoogleSignInException catch (e) {
if (e.code == GoogleSignInExceptionCode.canceled) {
throw Exception('Sign-in cancelled');
}
rethrow;
}
}รูปแบบนี้ใช้ได้กับผู้ให้บริการ OAuth ทั้งหมด: รับโทเค็นของผู้ให้บริการ ห่อหุ้มไว้ใน credential ของ Firebase แล้วเรียก signInWithCredential เมื่อแอปต้องเรียก API ของ Google ด้วย (Drive, Calendar) access token จะได้มาจากอีกขั้นตอนหนึ่งคือ authorizationClient.authorizeScopes ตามที่อธิบายไว้ในคู่มือการย้ายเวอร์ชันของ google_sign_in
ผู้สมัครที่เขียน GoogleSignIn().signIn() และอ่าน accessToken จาก authentication กำลังอ้างถึง API ก่อนเวอร์ชัน 7.0 ใน v7 นั้น authenticate() คืนค่าบัญชี authentication มีเพียง idToken และ access token สำหรับ scope เพิ่มเติมต้องเรียกการอนุญาตสิทธิ์อย่างชัดเจน
การดำเนินการ CRUD ของ Firestore และการสร้างแบบจำลองข้อมูล
Cloud Firestore จัดระเบียบข้อมูลเป็น collection และ document ต่างจากฐานข้อมูล SQL ตรงที่ Firestore ไม่มี schema เอกสารแต่ละฉบับสามารถมีฟิลด์ที่แตกต่างกันได้ ความยืดหยุ่นนี้เหมาะกับแอป Flutter แต่ต้องการการสร้างแบบจำลองข้อมูลอย่างมีวินัยเพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพ
การเขียนและการอ่าน Document
การดำเนินการของ Firestore ทำได้อย่างตรงไปตรงมาด้วยแพ็กเกจ cloud_firestore ทุกการอ่านและการเขียนจะมุ่งเป้าไปที่เส้นทางของ document ที่เฉพาะเจาะจง
import 'package:cloud_firestore/cloud_firestore.dart';
class TaskService {
final _db = FirebaseFirestore.instance;
final String _collection = 'tasks';
// Create a new document with auto-generated ID
Future<String> createTask(String userId, String title) async {
final doc = await _db.collection(_collection).add({
'userId': userId,
'title': title,
'completed': false,
'createdAt': FieldValue.serverTimestamp(),
});
return doc.id;
}
// Read a single document by ID
Future<Map<String, dynamic>?> getTask(String taskId) async {
final snapshot = await _db.collection(_collection).doc(taskId).get();
return snapshot.data();
}
// Update specific fields without overwriting the entire document
Future<void> toggleComplete(String taskId, bool completed) async {
await _db.collection(_collection).doc(taskId).update({
'completed': completed,
'updatedAt': FieldValue.serverTimestamp(),
});
}
// Delete a document
Future<void> deleteTask(String taskId) async {
await _db.collection(_collection).doc(taskId).delete();
}
}FieldValue.serverTimestamp() รับประกัน timestamp ที่สอดคล้องกันในทุกอุปกรณ์ โดยใช้นาฬิกาของเซิร์ฟเวอร์ Firestore แทนนาฬิกาของไคลเอนต์ ตั้งแต่ cloud_firestore 7.1.0 มี FieldValue.minimum และ FieldValue.maximum เพิ่มเข้ามาในกลุ่มการแปลงค่าฝั่งเซิร์ฟเวอร์ ทั้งสองตัวเก็บค่าที่ต่ำกว่าหรือสูงกว่าระหว่างค่าที่บันทึกไว้กับค่าที่ส่งเข้ามาโดยไม่ต้องอ่านก่อน เหมาะกับตัวนับอย่างคะแนนสูงสุด
สตรีมแบบเรียลไทม์ด้วย snapshots()
Firestore โดดเด่นด้วยการซิงโครไนซ์แบบเรียลไทม์ เมธอด snapshots() คืนค่า Stream ที่ส่งข้อมูลใหม่ทุกครั้งที่ document ที่เกี่ยวข้องมีการเปลี่ยนแปลง โดยไม่ต้อง polling
import 'package:cloud_firestore/cloud_firestore.dart';
class TaskStream {
final _db = FirebaseFirestore.instance;
// Stream all tasks for a specific user, ordered by creation date
Stream<List<Map<String, dynamic>>> userTasks(String userId) {
return _db
.collection('tasks')
.where('userId', isEqualTo: userId)
.orderBy('createdAt', descending: true)
.snapshots()
.map((snapshot) => snapshot.docs.map((doc) {
final data = doc.data();
data['id'] = doc.id; // Include document ID
return data;
}).toList());
}
}ภายใน widget ของ Flutter สตรีมนี้ผสานรวมโดยตรงกับ StreamBuilder เพื่อสร้าง UI ใหม่ในทุกการเปลี่ยนแปลงของฐานข้อมูล Firestore จัดการการเชื่อมต่อ การแคช และการคงอยู่แบบออฟไลน์โดยอัตโนมัติ
พร้อมที่จะพิชิตการสัมภาษณ์ Flutter แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
กฎความปลอดภัยของ Firestore สำหรับแอป Flutter
กฎความปลอดภัยทำงานบนเซิร์ฟเวอร์ Firestore และควบคุมสิทธิ์การอ่าน/เขียนในระดับ document หากไม่มีกฎที่เหมาะสม ผู้ใช้ที่ยืนยันตัวตนแล้วทุกคนสามารถอ่านหรือแก้ไข document ใดก็ได้
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can only access their own profile
match /users/{userId} {
allow read, update: if request.auth != null
&& request.auth.uid == userId;
allow create: if request.auth != null;
allow delete: if false; // Prevent self-deletion
}
// Tasks belong to the user who created them
match /tasks/{taskId} {
allow read, write: if request.auth != null
&& resource.data.userId == request.auth.uid;
allow create: if request.auth != null
&& request.resource.data.userId == request.auth.uid;
}
}
}ตัวแปร request.auth.uid มี ID ของผู้ใช้ที่ยืนยันตัวตนแล้วจาก Firebase Auth กฎที่อ้างอิง resource.data จะตรวจสอบฟิลด์ของ document ที่มีอยู่ ในขณะที่ request.resource.data จะตรวจสอบข้อมูลการเขียนที่กำลังเข้ามา
Firestore จะปฏิเสธการเข้าถึงทั้งหมดเป็นค่าเริ่มต้นในโหมด production ทุก collection ต้องมีกฎที่ชัดเจน ข้อผิดพลาดที่พบบ่อยในโปรเจกต์ Flutter: การพัฒนาด้วยกฎโหมดทดสอบ (allow read, write: if true) แล้วลืมล็อกก่อนนำขึ้น production
การคงอยู่แบบออฟไลน์และกลยุทธ์การแคช
Firestore เปิดใช้งานการคงอยู่แบบออฟไลน์เป็นค่าเริ่มต้นบนแพลตฟอร์มมือถือ เมื่ออุปกรณ์สูญเสียการเชื่อมต่อ การดำเนินการอ่านจะคืนค่าข้อมูลที่แคชไว้ และการดำเนินการเขียนจะเข้าคิวภายในเครื่องจนกว่าการเชื่อมต่อจะกลับมา
พฤติกรรมนี้ไม่ต้องเขียนโค้ด Flutter เพิ่มเติม การเรียก get() และ snapshots() เดิมยังคงทำงานได้ขณะออฟไลน์ อย่างไรก็ตาม มีสองสิ่งที่ต้องพิจารณาสำหรับแอป production:
- ขนาดแคช: Firestore แคช document ทั้งหมดที่ไคลเอนต์ได้อ่าน สำหรับแอปที่มีข้อมูลจำนวนมาก ให้กำหนดค่า
Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED)หรือกำหนดขีดจำกัดที่เฉพาะเจาะจง - ตัวบ่งชี้การเขียนที่รอดำเนินการ: ใช้
SnapshotMetadata.hasPendingWritesเพื่อแสดงให้ผู้ใช้ทราบว่าการเปลี่ยนแปลงในเครื่องยังไม่ได้ซิงค์ไปยังเซิร์ฟเวอร์
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('tasks')
.doc(taskId)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const CircularProgressIndicator();
final data = snapshot.data!;
final isPending = data.metadata.hasPendingWrites;
return Row(
children: [
Text(data['title']),
if (isPending) const Icon(Icons.cloud_upload, size: 16),
],
);
},
)การจัดโครงสร้างแอป Flutter Firebase สำหรับ Production
สถาปัตยกรรมที่ขยายขนาดได้จะแยกตรรกะของ Firebase ออกจากโค้ด UI รูปแบบ repository มอบชั้นการนามธรรมที่ชัดเจนซึ่งช่วยให้การทดสอบและการย้ายแบ็กเอนด์ในอนาคตง่ายขึ้น
abstract class TaskRepository {
Future<String> create(String userId, String title);
Stream<List<Task>> watchAll(String userId);
Future<void> update(String id, Map<String, dynamic> fields);
Future<void> delete(String id);
}
// firebase_task_repository.dart
class FirebaseTaskRepository implements TaskRepository {
final _db = FirebaseFirestore.instance;
Future<String> create(String userId, String title) async {
final doc = await _db.collection('tasks').add({
'userId': userId,
'title': title,
'completed': false,
'createdAt': FieldValue.serverTimestamp(),
});
return doc.id;
}
Stream<List<Task>> watchAll(String userId) {
return _db
.collection('tasks')
.where('userId', isEqualTo: userId)
.orderBy('createdAt', descending: true)
.snapshots()
.map((s) => s.docs.map(Task.fromFirestore).toList());
}
// ... update and delete implementations
}ด้วยรูปแบบนี้ โซลูชันการจัดการ state อย่าง Riverpod หรือ BLoC จะใช้อินเทอร์เฟซ repository โดยไม่ต้องรู้เกี่ยวกับ Firestore การทดสอบหน่วยสามารถแทนที่ด้วย mock repository ที่คืนค่าข้อมูลที่กำหนดไว้ล่วงหน้าได้
อัปเกรดเป็น FlutterFire 5: สิ่งที่พัง
รุ่นหลักเดือนตุลาคม 2026 (BoM 5.0.0, firebase_auth 7.0.0, cloud_firestore 7.0.0, firebase_core 5.0.0) เปลี่ยนข้อกำหนดในการ build มากกว่า API ของ Dart จุดที่ทำให้ build ล้มเหลวหรือไม่ผ่านการรีวิวมีดังนี้
- Android
minSdk24: ปลั๊กอินสร้างบน Firebase Android BoM 35 โปรเจกต์ที่ยังใช้minSdk21 หรือ 23 จะคอมไพล์ไม่ผ่าน - Apple SDK 13: Firebase Apple SDK ขยับเป็น 13.0.0 เมื่อใช้ Swift Package Manager (12.19.0 เมื่อใช้ CocoaPods) และแพ็กเกจ Swift ต้องใช้ macOS 11
- App Check: พารามิเตอร์ของ
activateเปลี่ยนชื่อเป็นproviderWeb,providerAndroidและproviderApple - Deprecation:
setIndexConfigurationFromJSONถูกประกาศ deprecated ในcloud_firestore7.0.0 และfirebase_ml_model_downloaderไม่ถูกเผยแพร่อีกต่อไป - ถูกลบไปแล้วตั้งแต่สาย 6.x:
fetchSignInMethodsForEmail()และUser.updateEmail()โค้ดที่ตรวจสอบว่าอีเมลใช้ผู้ให้บริการใดก่อนเข้าสู่ระบบต้องออกแบบใหม่ เพราะการป้องกัน email enumeration จะซ่อนข้อมูลนั้นไว้
รายการทั้งหมดแยกตามแพ็กเกจอยู่ในบันทึกประจำรุ่นของ FlutterFire และ changelog ของ cloud_firestore
คำถามสัมภาษณ์ Firebase สำหรับนักพัฒนา Flutter
การสัมภาษณ์ทดสอบทั้งความเข้าใจเชิงแนวคิดและการนำบริการ Firebase ไปใช้จริง คำถามต่อไปนี้ปรากฏเป็นประจำในการสัมภาษณ์นักพัฒนา Flutter ในปี 2026 ส่วนด้านเฟรมเวิร์กมีอยู่ในคู่มือคำถามสัมภาษณ์ Flutterที่ครอบคลุมกว้างกว่า
authStateChanges() แตกต่างจาก idTokenChanges() และ userChanges() อย่างไร?
authStateChanges() ทำงานเฉพาะในเหตุการณ์การเข้าสู่ระบบและออกจากระบบเท่านั้น idTokenChanges() จะทำงานเพิ่มเติมเมื่อ ID token ถูกรีเฟรช (ประมาณทุกหนึ่งชั่วโมง) userChanges() จับทุกอย่างข้างต้นรวมถึงการอัปเดตโปรไฟล์อย่าง updateDisplayName() สำหรับ navigation guard ส่วนใหญ่ authStateChanges() ก็เพียงพอแล้ว
เกิดอะไรขึ้นเมื่อมีการเขียน Firestore ขณะที่อุปกรณ์ออฟไลน์?
การเขียนจะถูกเก็บไว้ในแคชภายในเครื่องและคิวการเขียนที่รอดำเนินการ ตัวรับฟัง snapshots() จะได้รับข้อมูลที่อัปเดตทันทีพร้อม metadata.hasPendingWrites == true เมื่อการเชื่อมต่อกลับมา Firestore จะซิงค์การเขียนที่รอดำเนินการโดยอัตโนมัติ และการเขียนที่ไปถึงเซิร์ฟเวอร์เป็นลำดับสุดท้ายจะเป็นค่าที่ใช้
ควรสร้างแบบจำลองข้อมูล Firestore สำหรับแอปแชทอย่างไร?
ใช้ collection chats ที่แต่ละ document มี metadata (ผู้เข้าร่วม, timestamp ของข้อความล่าสุด) และ subcollection messages ภายใต้แต่ละ document ของแชท โครงสร้างนี้ช่วยให้สามารถ query รายการแชทของผู้ใช้ได้อย่างมีประสิทธิภาพ พร้อมทั้งแบ่งหน้าข้อความภายในแต่ละบทสนทนา ควร denormalize ข้อความล่าสุดเข้าไปใน document แม่ เพื่อหลีกเลี่ยงการอ่าน subcollection สำหรับมุมมองรายการ
ทำไมจึงควรใช้ FieldValue.serverTimestamp() มากกว่า DateTime.now()?
DateTime.now() ใช้นาฬิกาของอุปกรณ์ ซึ่งอาจไม่แม่นยำหรือถูกดัดแปลงได้ FieldValue.serverTimestamp() ใช้นาฬิกาของเซิร์ฟเวอร์ Firestore ซึ่งรับประกันลำดับที่สอดคล้องกันในทุกไคลเอนต์ สิ่งนี้สำคัญสำหรับฟีเจอร์อย่างการเรียงลำดับข้อความ ฟีดกิจกรรม และบันทึกการตรวจสอบ
การ query แบบ composite จัดการอย่างไรใน Firestore และมีข้อกำหนดเรื่อง index อย่างไร?
Firestore ต้องการ composite index สำหรับ query ที่รวมหลายฟิลด์ด้วยอนุประโยค where และ orderBy index แบบฟิลด์เดียวจะถูกสร้างโดยอัตโนมัติ แต่ composite index ต้องถูกกำหนดด้วยตนเองใน firestore.indexes.json หรือผ่าน Firebase console Firestore จะบันทึกลิงก์โดยตรงเพื่อสร้าง index ที่ขาดหายไปเมื่อ query ล้มเหลว
ควรใช้ transaction แทน batched write เมื่อใด?
ต้องใช้ transaction เมื่อการเขียนขึ้นอยู่กับข้อมูลที่อ่านในการดำเนินการเดียวกัน เช่น ลดจำนวนสต็อกเฉพาะเมื่อยังมากกว่าศูนย์ SDK จะเรียก callback ซ้ำหาก document ที่อ่านไปมีการเปลี่ยนแปลงก่อน commit ดังนั้น callback ต้องไม่มี side effect ส่วน batched write จะเขียนหลายรายการแบบ atomic โดยไม่มีการอ่านเลย เช่น สร้าง document หนึ่งพร้อมอัปเดตตัวนับในอีกที่หนึ่ง ทั้งสองแบบทำงานต่างกันเมื่อออฟไลน์: batch จะถูกเข้าคิว ส่วน transaction จะล้มเหลวหากไม่มีการเชื่อมต่อ
final db = FirebaseFirestore.instance;
// Transaction: read then write, retried on contention
await db.runTransaction((tx) async {
final ref = db.collection('products').doc(id);
final snap = await tx.get(ref);
final stock = snap.get('stock') as int;
if (stock <= 0) throw Exception('Out of stock');
tx.update(ref, {'stock': stock - 1});
});
// Batched write: atomic writes, no reads
final batch = db.batch();
batch.set(db.collection('orders').doc(), {'productId': id});
batch.update(db.collection('stats').doc('orders'), {
'count': FieldValue.increment(1),
});
await batch.commit();จะสร้างบทบาท admin ด้วย Firebase อย่างไร?
ใช้ custom claims ที่ตั้งค่าจากสภาพแวดล้อมที่เชื่อถือได้ผ่าน Admin SDK และไม่ตั้งจากไคลเอนต์ Flutter เด็ดขาด claim จะเดินทางไปพร้อมกับ ID token ทำให้กฎอ่านค่าได้ในรูป request.auth.token.admin == true โดยไม่ต้องค้นหา document เพิ่ม เอกสาร custom claims จำกัด payload ไว้ที่ 1000 ไบต์ และไคลเอนต์จะเห็น claim ใหม่หลังจากโทเค็นถูกรีเฟรชแล้วเท่านั้น ซึ่งสามารถบังคับได้ด้วย currentUser.getIdToken(true)
กฎความปลอดภัยกับ App Check ต่างกันอย่างไร?
กฎความปลอดภัยตัดสินว่าผู้ใช้คนหนึ่งสามารถอ่านหรือเขียน document หนึ่งได้หรือไม่ ส่วน App Check ยืนยันว่าคำขอมาจากแอปตัวจริงบนอุปกรณ์จริง ซึ่งช่วยบล็อกสคริปต์ที่นำคีย์ API สาธารณะไปใช้ซ้ำ ทั้งสองตอบคำถามคนละข้อ แอป production จึงต้องมีทั้งคู่ ตั้งแต่ FlutterFire 5 การเปิดใช้งาน App Check ใช้ providerAndroid, providerApple และ providerWeb
ทำไมหน้าจอที่ใช้ Firestore จึงอาจมีค่าใช้จ่ายสูง และแบ่งหน้าอย่างไร?
Firestore คิดค่าบริการต่อการอ่าน การเขียน และการลบ document แต่ละครั้ง listener บน collection ที่ไม่จำกัดขอบเขตจะอ่านทุก document ที่ตรงเงื่อนไขในการโหลดครั้งแรก จากนั้นนับหนึ่งการอ่านต่อ document ที่เปลี่ยนแปลง การแบ่งหน้าใช้ query cursor แทน offset: limit(20) สำหรับหน้าแรก แล้วใช้ startAfterDocument(lastDoc) สำหรับหน้าถัดไป ทำให้แต่ละหน้าเสียค่าใช้จ่ายเฉพาะ document ที่คืนกลับมาเท่านั้น
Firestore หรือ Realtime Database: ควรเลือกตัวไหนและเพราะอะไร?
Firestore เก็บ document ไว้ใน collection รองรับ query แบบผสมพร้อม index และคิดค่าบริการต่อการดำเนินการ Realtime Database เก็บข้อมูลเป็น JSON tree เดียว รองรับ query แบบตื้นบน child key เพียงตัวเดียว และคิดค่าบริการหลัก ๆ ตามแบนด์วิดท์และพื้นที่จัดเก็บ ซึ่งเหมาะกับการอัปเดตเล็ก ๆ ที่เกิดถี่มากอย่างตัวบ่งชี้สถานะออนไลน์ แอป Flutter ใหม่ส่วนใหญ่เริ่มต้นด้วย Firestore และเพิ่ม Realtime Database เฉพาะสำหรับ presence
สำหรับการเตรียมตัวสัมภาษณ์ Flutter เพิ่มเติม โมดูลฝึกฝนเฉพาะทางครอบคลุมหัวข้อเหล่านี้ด้วยแบบทดสอบเชิงโต้ตอบและความท้าทายแบบจับเวลา
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แหล่งข้อมูลอ้างอิง
- บันทึกประจำรุ่นของ Firebase สำหรับ Flutter: เนื้อหาของ BoM 5.0.0 และ 5.1.0, minSdk 24, การเปลี่ยนชื่อพารามิเตอร์ App Check
- Changelog ของ cloud_firestore: Pipelines ใน 6.3.0,
FieldValue.minimum/maximumและระยะทางของfindNearestใน 7.1.0 - Changelog ของ firebase_auth:
validatePasswordใน 6.0.0, TOTP บน macOS ใน 6.1.0, การลบfetchSignInMethodsForEmail - คู่มือการย้ายเวอร์ชันของ google_sign_in: singleton,
initialize,authenticate, การอนุญาตสิทธิ์ที่แยกออกมาใน 7.0 - Custom claims ของ Firebase: ขีดจำกัด 1000 ไบต์และการรีเฟรชโทเค็น
สรุปประเด็นสำคัญของ Flutter Firebase สำหรับการสัมภาษณ์
- FlutterFire BoM 5.1.0 มาพร้อม
firebase_auth7.0.1 และcloud_firestore7.1.0 และกำหนดให้ใช้minSdkของ Android 24 google_sign_in7 แทนที่GoogleSignIn().signIn()ด้วยGoogleSignIn.instance.initialize()ตามด้วยauthenticate()และ Firebase ต้องการเพียงidTokenauthStateChanges()มอบสตรีมแบบ reactive สำหรับจัดการสถานะการเข้าสู่ระบบทั่วทั้งแอปโดยไม่ต้องตรวจสอบด้วยตนเอง- การซิงโครไนซ์แบบเรียลไทม์ผ่าน
snapshots()ขจัดการ polling และผสานรวมโดยตรงกับStreamBuilderเพื่อการอัปเดต UI โดยอัตโนมัติ - กฎความปลอดภัยเป็นสิ่งจำเป็นสำหรับ production ควรตรวจสอบ
request.auth.uidเทียบกับฟิลด์ความเป็นเจ้าของของ document เสมอ และใช้ custom claims สำหรับบทบาท - Transaction อ่านแล้วเขียนพร้อมการลองใหม่ batched write เขียนอย่างเดียว การแบ่งหน้าใช้ cursor เพราะการอ่าน document ทุกครั้งมีค่าใช้จ่าย
- การคงอยู่แบบออฟไลน์ทำงานบนมือถือได้โดยไม่ต้องเขียนโค้ดเพิ่ม ใช้
hasPendingWritesเพื่อสื่อสารสถานะการซิงค์ให้ผู้ใช้ทราบ - รูปแบบ repository แยก Firebase ออกจากตรรกะทางธุรกิจ ทำให้การทดสอบและการย้ายในอนาคตเป็นเรื่องง่าย
คุณหาบั๊กใน Flutter เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 9 พฤศจิกายน 2569
แท็ก
แชร์
บทความที่เกี่ยวข้อง

20 คำถามสัมภาษณ์งาน Flutter ที่พบบ่อยที่สุดสำหรับนักพัฒนาแอปมือถือ
เตรียมตัวสัมภาษณ์งาน Flutter ด้วยคำถาม 20 ข้อที่ถูกถามบ่อยที่สุด ครอบคลุม Widget, State Management, Dart, สถาปัตยกรรม และแนวทางปฏิบัติที่ดีที่สุด พร้อมคำอธิบายอย่างละเอียดและตัวอย่างโค้ด

Flutter: สร้างแอปพลิเคชันข้ามแพลตฟอร์มตัวแรก
คู่มือฉบับสมบูรณ์สำหรับการสร้างแอปพลิเคชันมือถือข้ามแพลตฟอร์มด้วย Flutter และ Dart ครอบคลุม Widget การจัดการสถานะ การนำทาง และแนวทางปฏิบัติที่ดีสำหรับผู้เริ่มต้น

Dart Isolates และ Concurrency 2026: compute, Async และคำถามสัมภาษณ์
คู่มือฉบับสมบูรณ์เกี่ยวกับ Dart isolates, ฟังก์ชัน compute ใน Flutter และ pattern ของ concurrency สำหรับการพัฒนาแอปที่ตอบสนองรวดเร็วในปี 2026