TypeScriptで書いているNext.jsアプリのユーザ情報管理を、 Firebase AuthenticationとFirestoreで行ったのですが、 調べたときに見かけたコードサンプルは、呼び出し方がJavaScriptのままだったので、 TypeScriptっぽいサンプルもここに残して置きます。
というかWeb v9の書き方じゃない・・・と後から気付きました・・・
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#web-v9
Firebase SDKの設定
TSの書き方では(もといWeb v9の書き方では)firebase.auth
のような呼び出し方をすることはできないので、
それぞれgetAuthやgetFirestoreを使ってインスタンスを取得する必要があります。
- libs/firebase.ts
import * as firebase from 'firebase/app'; import { Auth, getAuth } from 'firebase/auth'; import { Firestore, getFirestore } from 'firebase/firestore'; const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, }; if (typeof window !== 'undefined' && !firebase.getApps().length) { firebase.initializeApp(firebaseConfig); } export default firebase; export const getFirebaseAuth = (): Auth => getAuth(); export const getFirebaseDb = (): Firestore => getFirestore();
firebaseをexportしていますが、今のところこれを触ることはなかったので、いらないかもしれません。
Firebaseアクセス
次はAuthenticationとFirestoreへの接続です。
①Authenticationへのサインイン
signInWithPopupの呼び出しは、パラメータにAuthインスタンスと対象のプロバイダを渡すことで行なえます。
サインインが成功するとUserCredentialが返却されるので、ユーザ情報が必要な場合は、そのuser属性を取得してください。
import { GoogleAuthProvider, User, UserCredential, signInWithPopup } from 'firebase/auth'; import { getFirebaseAuth } from 'libs/firebase'; 〜〜〜 const auth = getFirebaseAuth(); const provider = new GoogleAuthProvider(); const signInResult: UserCredential = await signInWithPopup(auth, provider); const crrentUser: User = signInResult.user;
Auth.currentUserに関する注意
AuthインターフェイスにはcurrentUserがあるのですが、これはonAuthStateChangedの処理後でなければ値がnullになっているので、利用する場合は気を付けてください。
- https://stackoverflow.com/questions/40764070/firebaseauth-getauth-returns-a-null
- https://firebase.google.com/docs/auth/web/manage-users?hl=ja
②Firestoreへのアクセス
FirestoreもAuthenticationと同じように、getFirestore(ここではgetFirebaseDb)を使って処理を行います。 対象のコレクションとドキュメントを指定し、collection関数とdoc関数を使って、入れ子にして呼び出します。
doc(collection(Firestoreインスタンス, コレクション名), ドキュメントID)
これで得られるDocumentDataオブジェクトに、getDocで取得、setDocで追加の操作をします。
データ取得
getDocで取得したオブジェクトのdata()
を呼び出すと、コレクションの値が取得できます。
フィールドの値はDocumentData["フィールド名"]
の形で取得します。
import { DocumentData, collection, doc, getDoc } from 'firebase/firestore'; 〜〜〜 const db = getFirebaseDb(); const uid = "対象のドキュメントID"; const document = await getDoc(doc(collection(db, 'users'), uid)); if (document.exists()) { const documentId = document.id; // この場合、uidと同じ値 const documentData: DocumentData = document.data(); const user: UserType = { id: documentId, name: documentData['name'], email: documentData['email'], picture: documentData['picture'], token: documentData['token'], };
データ追加
ドキュメントをコレクションに追加するには、DocumentDataにsetDocを行います。
登録するデータはオブジェクトで定義してください。
const data = { name: "名前", email: "メールアドレス", picture: "アバターURL", token: "アクセストークン", }; await setDoc(対象のDocumentData, data);
③コード例
実際のコードだと以下のようになりました。
- functions/account.ts
import { GoogleAuthProvider, UserCredential, signInWithPopup } from 'firebase/auth'; import { Firestore, collection, doc, getDoc, setDoc } from 'firebase/firestore'; import Router from 'next/router'; import { convertToUser } from './user'; import { getFirebaseAuth, getFirebaseDb } from 'libs/firebase'; import { UserType } from 'application'; export const googleSignin = () => { (async () => { const auth = getFirebaseAuth(); const provider = new GoogleAuthProvider(); try { const signInResult: UserCredential = await signInWithPopup(auth, provider); const db = getFirebaseDb(); const exist = await existFirebaseUser(db, signInResult.user?.uid); if (!exist) { // firebaseへのユーザ登録処理 const user: UserType = await convertToUser(signInResult.user); const isRegistered = await registerUser(db, user); if (isRegistered) { console.log('登録OK uid: ' + user.id); } else { console.log('登録NG uid: ' + user.id); Router.push('/error'); } } Router.push('/mypage'); } catch (error) { console.log(error); console.log(error.code); console.log(error.message); } })(); }; const existFirebaseUser = async (db: Firestore, uid: string): Promise<boolean> => { const result = (await getDoc(doc(collection(db, 'users'), uid))).exists(); return result; }; const registerUser = async (db: Firestore, user: UserType): Promise<boolean> => { const userData = { name: user.name, email: user.email, picture: user.picture, token: user.token, }; try { await setDoc(doc(collection(db, 'users'), user.id), userData); console.log('登録成功'); return true; } catch (error) { console.log('登録失敗:' + error); return false; } }; export const signout = () => { getFirebaseAuth() .signOut() .then(() => { window.location.reload(); }); };
- functions/user.ts
import { User } from 'firebase/auth'; import { UserType } from 'application'; export const convertToUser = async (firebaseUser: User): Promise<UserType> => { if (firebaseUser) { const user: UserType = { id: firebaseUser.uid, name: firebaseUser.displayName, email: firebaseUser.email, picture: firebaseUser.photoURL, token: await firebaseUser.getIdToken(), }; return user; } return null; };