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を使ってインスタンスを取得する必要があります。
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になっているので、利用する場合は気を付けてください。
②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;
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);
③コード例
実際のコードだと以下のようになりました。
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) {
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();
});
};
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;
};