背景
Cloud Functionsと Node.js, TypeScript をまともにいじったことなかったのですが、ひょんなことから0から始める機会があったので、流れをまとめました。
開発エディタには TypeScript と相性の良い VSCode(Visual Studio Code)を選びました。
読者対象
- Mac
- Firebase ちょっと使ったことある
- Firebase Functions 未経験
- Node.js 未経験
- TypeScript 未経験
書いてること
TypeScript で書いたコードが Firebase-Tools で Cloud Functions にデプロイできるようになるまでについてまとめました。
書いていないこと
- Node.js のイディオムやコードに関すること
- TypeScript のイディオムやコードに関すること
全体の流れ
- Node.js 用バージョン管理ツール( nvm )のインストール
- nvm で Firebase が推奨するバージョンをインストール
- npm で Firebase-Tools パッケージをインストール
- Firebase-Tools でプロジェクト作成
- npm で残りの必要パッケージをインストール
- TypeScriptに必要なパッケージやトランスパイル周りについて
- Firebase-Tools でデプロイ
- VSCodeで開発しやすい環境準備
- その他(調査中に見つけた情報)
Node.js 用バージョン管理ツール( nvm )のインストール
nvm, nodenv, n, ndenv と色々ありますが、nvm を選択しました。
理由は Google トレンドの結果をシンプルに受け止めました。
こういったエコシステムは流行りよりも情報量で選んで、ささっと切り抜けないと本来の目的へ辿りつくのに時間がかかるリスクが増すためです。
Mac なので Homebrew でインストールします。
インストール成功すると追加設定するよう言われるので、言われたとおりやります。
$ brew install nvm
# ↓ インストール成功後
$ mkdir ~/.nvm
$ vim ~/.bash_profile
# ↓の2つを.bash_profileに追記します
export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh
# インストール成功したか確認します
$ nvm --version
0.34.0
最後の確認でうまく動かない人はターミナルを再起動するか $ source ~/.bash_profile
でリロードしてみてください。
nvm で Firebase が推奨するバージョンをインストール
公式ページでは 8.15.0
なのでローカルの Node.js のバージョンも合わせます。
$ nvm install v8.15.0
npm で Firebase-Tools パッケージをインストール
npm とは Node.js 用パッケージマネージャーです。
他言語のパッケージマネージャー同様に、グローバルとローカルのインストール先を指定できます。
まず必要なパッケージは firebase-tools
になります。
これがあれば Firebase に必要な開発環境の雛形を簡単に作ってくれます。
通常の Node.js 開発であれば $ npm init
を叩けば Node.js プロジェクトが作られますし、そのときに package.json
も一緒に作られます。
しかし $ firebase init
を叩けば Node.js ベースの Firebase のプロジェクトを作ってくれます。
firebase-tools
パッケージはグローバルにインストールします。
-g
を指定することでグローバルパッケージとしてインストールされ、いろんな所で直接呼べるようになります。
$ npm install -g firebase-tools
※ グローバル環境を汚したくない人は後述する方法を参考にしてみてください。
残りの必要パッケージをインストールする前に先に Firebase プロジェクトを作成します。
そうすることで package.json
が作成されて、そこにローカルパッケージとして追加できるからです。
Firebase-Tools でプロジェクト作成
firebase-toolsからFirebaseを操作するために認可させる必要があります。
$ firebase login
プロジェクト作成は
$ firebase init
実行すると質問されるので答えていくことで、最後に Firebase 開発に必要なファイル群が作成されます。
例えば
- Cloud Functions のみ
- default Firebase プロジェクトの指定
- TypeScript
- TSLintあり
- 依存解決する
とした場合の ファイルツリーと package.json
は次のようになります。
$ tree -L 2
.
├── firebase.json
└── functions
├── node_modules
├── package-lock.json
├── package.json
├── src
├── tsconfig.json
└── tslint.json
package.json
の中身はこうなります。
{
"name": "functions",
"scripts": {
"lint": "tslint --project tsconfig.json",
"build": "tsc",
"serve": "npm run build && firebase serve --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "8"
},
"main": "lib/index.js",
"dependencies": {
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.0.0"
},
"devDependencies": {
"tslint": "^5.12.0",
"typescript": "^3.2.2"
},
"private": true
}
TypeScript
や TSLint
と答えたことで依存パッケージが入っています。
また開発中しか使わないので devDependencies
スコープになります。
devDependencies
とは $ npm install --production
のように本番向けだとインストールされません。
--save
だと dependencies
スコープ
--save-dev
だと devDependencies
スコープ
に入ります。
残りのパッケージをインストール
例えば typed-rest-client
パッケージをインストールすると package.json
に追記されます。
$ npm i --save typed-rest-client
↓は package.json
の抜粋です。このように先程インストールしたパッケージが追記されます。
{
"dependencies": {
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.0.0",
"typed-rest-client": "^1.5.0"
}
}
express
パッケージも入れる場合は入れましょう。
グローバル環境を汚染したくない人向け
これが正しい方法か自信そんなにないですが、
-
$ npm init
でpackage.json
作成 -
$ npm install --save-dev firebase-tools
でpackage.json
に追記 -
$ npm install
でfirebase-tools
をインストール -
$ npx firebase init
で Firebase のプロジェクト作成 - 作成された
package.json
ファイルがあるディレクトリ上で$ npm install --save-dev firebase-tools
をインストール
npx
とは ローカルパッケージを実行するコマンドです。
他メンバーが入ってきたら
nvm のインストールは必要ですが、 npm のパッケージに関しては
$ npm install
と叩くだけで package.json
内に記述されたパッケージ一覧をインストールしてくれます。
TypeScriptに必要なパッケージやトランスパイル周りについて
トランスパイル
TypeScriptはご存知の通り型あり言語です。
そのままでは動かないので JavaScript にトランスパイルする必要があります。
トランスパイルには tsc
というパッケージを使います。
$ tsc src/ts/app.ts --outDir dist/ts
とすることで JavaScript ファイルに変換されます。
変換後の JavaScript を Firebase にデプロイします。
つまりデプロイ前にトランスパイルが必須となります。
firebase-tools
によって TypeScript を指定して作成したプロジェクトはそこらへんの細かいファイル設定を全て作成してくれます。
tsconfig.json
だけでなく package.json
の script
にも反映されています。
"scripts": {
"lint": "tslint --project tsconfig.json",
"build": "tsc",
"serve": "npm run build && firebase serve --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
}
script
とは $ npm run
で実行できるコマンド一覧を指します。
新しく作ることもできます。
$ firebase init
で TypeScript を指定してあると、ここで事前に npm run build
でトランスパイルされてから
それぞれのデプロイやローカル実行などが動きます。
型情報
JavaScriptのパッケージを使うには型情報(DefinitelyTyped) が必要です。
過去に TypeScript 1系などでは、 tsd
や Typings
というツールを使って
型情報管理を行っていましたが、 TypeScript 2系になってからはそれらツールは不要で
npm 内で完結できるようになりました。
例えば Cloud Functions や Express, Node といった型情報をインストールしたい場合
$ npm install --save-dev @firebase/functions-types
$ npm install --save-dev @types/express
$ npm install --save-dev @types/node
のように @types/[型情報]
とすることで型情報をインストールできます。
型情報が用意されているかどうかは、http://definitelytyped.org/ で調べることができます。
パッケージによっては始めから型情報が入っている場合もあります。
コーディングスタイル
コーディングスタイルはここを参考にしました。
Firebase-Tools でデプロイ
書いてみる
functions/src/index.ts
に書いてみましょう。
エディタにはこだわりなければ VSCode
をおすすめします。
import * as functions from 'firebase-functions'
export const helloWorld = functions.https.onRequest( (request, response) => {
console.log('Hello world')
response.status(200).send('Hello world')
}
デプロイ
$ firebase deploy --only functions
$ firebase init
で Firebase プロジェクトを選択済みであれば、このコマンドだけでデプロイが完了します。
async/awaitを使う
下のコードは POSTリクエストを投げると Firestore にデータを書き込むサンプルです。
このサンプルは Firestore を使うため、 プロジェクトに Firestoreに関する情報が配置されている必要があります。
振り返りとして、もう一度 $ firebase init
でプロジェクトを作り Firestore も選択したプロジェクトで試してみるといいです。
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp(functions.config().firebase);
export const helloWorld = functions.https.onRequest( async (request, response) => {
if (request.method !== 'POST') {
const json = {
error_list: {
error_code: '4000',
message: 'This is not post request'
}
}
response.status(400).send(json)
return
}
await admin.firestore().collection('products').doc('1').set({
first: "hanako",
last: "yamada"
})
response.status(200).send({first: "hanako", last: "yamada"})
}
VSCodeで開発しやすい環境準備
ローカル開発
$ firebase serve
でエミュレーターによるローカルで立ち上がり http://localhost~ などでアクセスできるようになります。
また npm script に↓が用意されているので, VSCode上で npm serve
を実行するとトランスパイルして立ち上げてくれます。
"serve": "npm run build && firebase serve --only functions,firestore"
その他(調査中に見つけた情報)
-
npm watch
を実行しながらnpm serve
を実行すると、コード変更がすぐに反映される -
$ firebase deploy
は--project
で Firebaseプロジェクトを指定できる. -
--project
で指定をエイリアスに出来る$ firebase use --add
を使う -
--project
を未指定でどのエイリアスが使われるのかは$ firebase use
で分かる - Functionsだけのデプロイは
$ firebase deploy --only functions
- 関数だけのデプロイは
$ firebase deploy --only functinos:helloWorld
- 関数以外のデプロイは
$ firebase deploy --except functions
- 関数の削除は
$ firebase functions:delete helloWorld
- 関数リネームは
$ firebase deploy --only functions:newHelloWorld
後$ firebase functions:delete helloWorld
- リージョン変更も同様の流れ
- npm のインストールパッケージ一覧は
$ npm ls
で確認できる - ルートパッケージだけの確認は
$ npm ls --depth=0
- グローバルパッケージは
$ npm -g ls
- Firestore エミュレータは
$ firebase setup:emulators:firestore
が必要 - エミュレーターがどのサービスをたちあげるかは
firebase.json
の中身に依存する
認証エラーはこちらを参考にしました
最後に
- 駆け足で調査したので情報漏れなどがあるかもしれません。
- Google公式漁るときは、ノートに情報を抜粋しながらまとめないと、GCP側とFirebase側とで資料と用語がぶつかり、点と点の間の線がやばいことになる