Симпатичный небольшой шаблон проектирования кода, который повысит производительность вашего Firestore и сделает ваш код безопасным с помощью TypeScript.

TL;DR:Просто посмотрите этот репозиторий, чтобы посмотреть пример кода:



Если вы ищете способ добавить TypeScript в SDK Firebase V8, ознакомьтесь с другими моими статьями:





Давайте код!

Итак, недавно я поиграл с новым SDK Firebase/Firestore V9 и придумал очень простой и минимальный способ заставить его хорошо работать с TypeScript.

Единственное, что мы собираемся сделать, это создать абстракцию, которая сопоставляет все возможные коллекции в нашей БД и возвращает типизированную ссылку на коллекцию Firestore. Так как я поклонник Vue и парадигмы компонуемого кодирования, я собираюсь поместить эту абстракцию в каталог с именем composables и в файл с именем useDb.ts. Первое, что мы сделаем в этом файле, — инициализируем приложение firebase и получим ссылку на базу данных Firestore:

// /composables/useDb.ts
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()

Далее мы собираемся создать небольшую функцию, которая будет принимать имя коллекции и выдавать ссылку на коллекцию Firestore. Эта ссылка может быть передана в getDoc или getDocs и т. д. для чтения/записи данных:

// /composables/useDb.ts
// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, collection } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = (collectionName: string) => {
  return collection(firestore, collectionName)
}

Прямо сейчас эта функция на самом деле мало что делает и в некотором роде бессмысленна, ноа вот и магия... Мы собираемся сделать эту функцию универсальным типом и добавить правильный тип в коллекцию. ссылка, которую он возвращает:

// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, CollectionReference, collection, DocumentData } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = <T = DocumentData>(collectionName: string) => {
  return collection(firestore, collectionName) as CollectionReference<T>
}

Довольно просто. Довольно лаконично.

Теперь мы импортируем все наши типы из любого места, где они определены, и экспортируем набор констант, запускающих функцию createCollection, с правильным путем и типом коллекции:

// Get the imports
import { initializeApp } from 'firebase/app'
import { getFirestore, CollectionReference, collection, DocumentData } from 'firebase/firestore'
// Init the firebase app
export const firebaseApp = initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
// Export firestore incase we need to access it directly
export const firestore = getFirestore()
// This is just a helper to add the type to the db responses
const createCollection = <T = DocumentData>(collectionName: string) => {
  return collection(firestore, collectionName) as CollectionReference<T>
}
// Import all your model types
import { User } from 'src/types/User'
import { Author } from 'src/types/Author'
import { Book } from 'src/types/Book'
// export all your collections
export const usersCol = createCollection<User>('users')
export const authorsCol = createCollection<Author>('authors')
export const booksCol = createCollection<Book>('books')

Теперь у вас есть только один список имен коллекций и их типов!

Итак, как мне это использовать?

Ну, это так просто, как кажется! Допустим, мы хотим добавить нового пользователя в базу данных, мы просто импортируем usersCol из useDb и используем его в качестве первого аргумента функции Firestore setDoc:

// /someOtherFile.ts
import { doc, setDoc } from '@firebase/firestore'
import { usersCol } from './composables/useDb'
export const setJamiesUser = async () => {
  const userRef = doc(usersCol, 'user_12345')
  await setDoc(userRef, {
    age: 30,
    firstName: 'Jamie',
    lastName: 'Curnow'
  })
}

Если вы попробуете это сделать, вы увидите, что мы получаем подсказки типов и ошибки компилятора, если мы даем setDoc неправильный тип данных для пользователя!

Другой пример: получение данных... Допустим, мы хотим получить все документы из коллекции books в базе данных и console.log() заголовков каждой книги:

// /anotherFile.ts
import { getDocs } from '@firebase/firestore'
import { booksCol } from './composables/useDb'
export const logBookTitles = async () => {
  const bookDocs = await getDocs(booksCol)
  bookDocs.docs.forEach((bookDoc) => {
    const book = bookDoc.data()
    console.log(book.title)
  })
}

Здесь typescript знает, что bookDoc.data() возвращает объект типа Book, и позволяет нам записать название книги. 🥳

Использование этого способа также будет работать для глубоко вложенных ключей при обновлении документов, что совершенно потрясающе:

import { doc, updateDoc } from '@firebase/firestore'
import { booksCol } from './composables/useDb'
export const updateBook = async () => {
  const bookDocRef = doc(booksCol, 'book_12345')
  await updateDoc(bookDocRef, {
    'meta.created': new Date().toISOString()
  })
}

В этом примере набирается meta.created! Удивительно!

Подведение итогов

Это такая приятная функция нового SDK V9 Firestore, и команда Firestore проделала фантастическую работу по ее созданию 🙌.

Если вы хотите увидеть минимальный репозиторий, реализующий этот шаблон, оформите заказ и поставьте звездочку:



Не забудьте немного поаплодировать этой истории, если она показалась вам полезной — ваши аплодисменты заставляют меня публиковать контент и дают мне небольшой стимул писать больше каждый раз, когда я вижу, что что-то выходит!

Поддержите своих создателей!

Если вы нашли эту историю полезной и хотели бы, чтобы я продолжал писать полезный контент, пожалуйста, поддержите меня на Patreon 🤗



Дополнительные материалы на plainenglish.io