The Firebase Blog: Launching Cloud Functions for Firebase v1.0 にあるように、 Cloud Functions の SDK が 1.0 になった。と言っても SDK が 1.0 になっただけで、 Cloud Functions のベータが外れたわけではない。
Migration
Firebase SDK for Cloud Functions Migration Guide: Beta to version 1.0 | Firebase という親切なドキュメントがある。
Firestore を使っている Project で、実際に 1.0 に Migration した。
noImplicitAny
まず、 TypeScript で使おうとするとエラーが出る。
any 型が使われてしまっているようで、 tsconfig に "skipLibCheck": true
を追加してひとまずしのぐようにした。
New initialization syntax for firebase-admin
今まではこう書いていた admin の initializeApp が簡潔になった。
// before admin.initializeApp(functions.config().firebase) // after admin.initializeApp()
こうなったのは、 functions.config().firestore
が廃止され process.env.FIREBASE_CONFIG
を使うようになったからのようだ。
今後 projectId などが必要な場合は環境変数から取るように、と書かれている。
let firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); /* { databaseURL: 'https://databaseName.firebaseio.com', storageBucket: 'projectId.appspot.com', projectId: 'projectId' } */
SDK changes by trigger type
今までの Event trigger では、 event
というパラメータだけを使っていたが、それが context
と snapshot
(or change
) という 2 つのパラメータに変更された。
// before exports.dbWrite = functions.firestore.document('/path').onWrite((event) => { const beforeData = event.data.previous.data(); // data before the write const afterData = event.data.data(); // data after the write }); // after exports.dbWrite = functions.firestore.document('/path').onWrite((change, context) => { const beforeData = change.before.data(); // data before the write const afterData = change.after.data(); // data after the write });
context
型定義はこうなっている。
/** The context in which an event occurred. * An EventContext describes: * - The time an event occurred. * - A unique identifier of the event. * - The resource on which the event occurred, if applicable. * - Authorization of the request that triggered the event, if applicable and available. */ export interface EventContext { /** ID of the event */ eventId: string; /** Timestamp for when the event occured (ISO string) */ timestamp: string; /** Type of event */ eventType: string; /** Resource that triggered the event */ resource: Resource; /** Key-value pairs that represent the values of wildcards in a database reference */ params: { [option: string]: any; }; /** Type of authentication for the triggering action, valid value are: 'ADMIN', 'USER', * 'UNAUTHENTICATED'. Only available for database functions. */ authType?: 'ADMIN' | 'USER' | 'UNAUTHENTICATED'; /** Firebase auth variable for the user whose action triggered the function. Field will be * null for unauthenticated users, and will not exist for admin users. Only available * for database functions. */ auth?: { uid: string; token: object; };
今までの event が持っていたパラメータに加えて、 Callable Functions の context が合わさったような感じ。 (Callable Functions の説明は こっち を参照)
snapshot
今までは DeltaDocumentSnapshot という型があったが、それが廃止され firebase.firestore.DocumentSnapshot
が使われるようになった。
型定義はこうなっているが、前から使っていたものだし特に変更点もないと思う。
/** * A `DocumentSnapshot` contains data read from a document in your Firestore * database. The data can be extracted with `.data()` or `.get(<field>)` to * get a specific field. * * For a `DocumentSnapshot` that points to a non-existing document, any data * access will return 'undefined'. You can use the `exists` property to * explicitly verify a document's existence. */ export class DocumentSnapshot { protected constructor(); /** True if the document exists. */ readonly exists: boolean; /** A `DocumentReference` to the document location. */ readonly ref: DocumentReference; /** * The ID of the document for which this `DocumentSnapshot` contains data. */ readonly id: string; /** * The time the document was created. Not set for documents that don't * exist. */ readonly createTime?: string; /** * The time the document was last updated (at the time the snapshot was * generated). Not set for documents that don't exist. */ readonly updateTime?: string; /** * The time this snapshot was read. */ readonly readTime: string; /** * Retrieves all fields in the document as an Object. Returns 'undefined' if * the document doesn't exist. * * @return An Object containing all fields in the document. */ data(): DocumentData | undefined; /** * Retrieves the field specified by `fieldPath`. * * @param fieldPath The path (e.g. 'foo' or 'foo.bar') to a specific field. * @return The data at the specified field location or undefined if no such * field exists in the document. */ get(fieldPath: string|FieldPath): any; /** * Returns true if the document's data and path in this `DocumentSnapshot` * is equal to the provided one. * * @param other The `DocumentSnapshot` to compare against. * @return true if this `DocumentSnapshot` is equal to the provided one. */ isEqual(other: DocumentSnapshot): boolean; }
change
onUpdate と onWrite 時は snapshot ではなく Change<DocumentSnapshot>
が使われる、Change の型を見てみる。
/** Change describes a change of state - "before" represents the state prior * to the event, "after" represents the state after the event. */ export declare class Change<T> { before: T; after: T; constructor(before?: T, after?: T); }
今まで event.data.previous
, event.data
とデータを取得していたが、それが before, after と取れるようになった。わかりやすくて良い。
おわり
こんな感じで Migration が完了した、特に大きな混乱もなく、動作も問題なさそう。 (noImplicitAny は困っているが...)
Cloud Functions はとにかく重複発火をなくしてほしい。