Firebase 101
The firebase auth
comes out of the box. For databases we use firestore
.
Setting up firebase
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
const firebaseConfig = {
apiKey: ADD_KEY,
authDomain: ADD_KEY,
projectId: ADD_KEY,
storageBucket: ADD_KEY,
messagingSenderId: ADD_KEY,
appId: ADD_KEY,
}
const app = initializeApp(firebaseConfig)
// intialize the firestore database
export const db = getFirestore()
CRUD in Firebase
Read
There are 2 parts to getting data in firestore
,
1st : Creating a reference of the document or collection (It is like creating a connection to the database, which will make sure that you get the latest updated data)
2nd : Fetching the data (snapshot)
Getting all the documents from a collection
// Create a reference of the users collection,
// collection is function in the firebase/firestore module
const usersRef = collection(db, 'users')
const usersSnap = await getDocs(userRef)
// Now the usersSnap contains an array of objects with the user data and id
// usersSnap = {id, data()}
// Extract all the users
const users = []
usersSnap.forEach((doc) => {
users.push({
id: doc.id,
data: doc.data(),
})
})
Getting / Reading selected documents using query
const usersRef = collection(db, 'users')
const q = query(usersRef, where('name', ==, 'adesh'))
const usersSnap = await getDocs(q)
const users = []
usersSnap.forEach(doc => {
users.push({
id : doc.id,
data : doc.data()
})
})
Can also add orderBy
or limit
:
const q = query(
listingsRef,
where('offer', '==', true),
orderBy('timestamp', 'desc'),
limit(10)
)
Reading a single document
const userRef = doc(db, 'users', user.id)
const userSnap = await getDoc(userRef)
if (docSnap.exists()) {
console.log(docSnap.data())
// setting the user state
setUser(docSnap.data())
}
Create
There are 2 function to create and add new documents in the collection : addDoc
& setDoc
Use setDoc
when you want to specify the id for the document yourself, Use addDoc
when you want firebase to generate an id for you.
setDoc
// To get reference of single document, use doc to get reference of entire collection use collection
const userRef = doc(db, 'users', user.uid)
// setDoc takes the userRef and the data object to add for it
await setDoc(userRef, { name: 'adesh', age: 21 })
addDoc
const usersRef = collection(db, 'users')
await addDoc(userRef, { name: 'adesh', age: 21 })
Notice that we have used the collection ref in the addDoc
, since we wanted an auto-generated id
Update
const updateUser = async (id, age) => {
const userDoc = doc(db, 'users', id)
// add only the updated field, rest will remain same
const newFields = { age: age }
await updateDoc(userDoc, newFields)
}
Delete
const deleteUser = async (id) => {
const userDoc = doc(db, 'users', id)
await deleteDoc(userDoc)
}
Sign In / Sign Up with Email and Password
Use this docs https://firebase.google.com/docs/auth/web/password-auth, its really simple.
const onSubmit = async (e) => {
e.preventDefault()
try {
const auth = getAuth()
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
)
if (userCredential.user) {
navigate('/')
}
} catch (error) {
// console.log(error)
toast.error('Bad User Credentials')
}
}
const onSubmit = async (e) => {
e.preventDefault()
try {
const auth = getAuth()
// Since the function doesn't take name
// we use the update profile function, which is used to update the name and photo of the user
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
)
// we'll use this user info to add to the database
const user = userCredential.user
updateProfile(auth.currentUser, {
displayName: name,
})
const formDataCopy = { ...formData }
delete formDataCopy.password
formDataCopy.timestamp = serverTimestamp()
await setDoc(doc(db, 'users', user.uid), formDataCopy)
navigate('/')
} catch (error) {
// console.log(error)
toast.error('Unable to Register')
}
}
Sign In with Google
const onGoogleClick = async () => {
try {
const auth = getAuth()
const provider = new GoogleAuthProvider()
// Opens the pop-up and returns the signed in User
const result = await signInWithPopup(auth, provider)
const user = result.user
const userRef = doc(db, 'users', user.uid)
const userSnap = await getDoc(userRef)
// if the user does not exist in the users collection create one
if (!userSnap.exists()) {
await setDoc(doc(db, 'users', user.uid), {
name: user.displayName,
email: user.email,
timestamp: serverTimestamp(),
})
}
navigate('/')
} catch (error) {
toast.error('Could not login with google')
}
}
Pagination
To set up pagination, fetch say 10 items on the page load (useEffect
). Keep track of the last fetched item
const [lastFetchedListing, setLastFetchedListing] = useState(null)
// ...
// Inside useEffect
const listingsRef = collection(db, 'listings')
// Create a query
const q = query(
listingsRef,
where('type', '==', params.categoryName),
orderBy('timestamp', 'desc'),
limit(10)
)
// Execute the query
const querySnap = await getDocs(q)
// Set the last fetched Item
const lastVisibleListing = querySnap.docs[querySnap.docs.length - 1]
setLastFetchedListing(lastVisibleListing)
const listings = []
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data(),
})
})
setListings(listings)
Now on LoadMoreClick
make a query using the startAfter
function.
const listingsRef = collection(db, 'listings')
const q = query(
listingsRef,
where('type', '==', params.categoryName),
orderBy('timestamp', 'desc'),
startAfter(lastFetchedListing), // query with startAfter
limit(10)
)
const querySnap = await getDocs(q)
// Update the last fetched Item
const lastVisibleListing = querySnap.docs[querySnap.docs.length - 1]
setLastFetchedListing(lastVisibleListing)
const listings = []
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data(),
})
})
// Make sure to add the new Items, with the previous listings and not remove the previous once by setting the state just to the new 10 fetched items
setListings((prevListings) => [...prevListings, ...listings])