目的
Webアプリのモックアップを開発する際にバックエンドとデータベースをさくっと手軽に用意したいと思ったので実装してみました。
Google Cloud Platform の無料枠を存分に使ってやりましょう💰
さくっと構築
GCPプロジェクト配下にFirestoreのデータベース&コレクションと Cloud Run Functions の関数(ランタイム: Python 3.12)を作成していきます。
たぶん5分あれば終わります。
Firestoreにデータベースを作成
モードはネイティブを選択してます。
データベースIDを (default)
で作成すると無料枠の対象になるのでそのままにします。
なお今回はさくっと動作確認することが目的なので、セキュリティルールはテストルールです。
データベースが作成できたらコレクションと最初のドキュメントを作っておきます。
これにてFirestoreは準備完了。
Cloud Run Functions を作成
作成するコード
以下4つのメソッドを持つ関数を作成します。
メソッド | CRUD | 処理 |
---|---|---|
POST | Create | リクエストされたJSONのキーと値を使ってドキュメントを作成。 |
GET | Read | コレクション直下のドキュメントを全件取得。 |
PUT | Update | リクエストされたJSONから id の値を取得し、該当するドキュメントを更新。 |
DELETE | Delete | リクエストされたJSONから id の値を取得し、該当するドキュメントを削除。 |
- GCPプロジェクトのIDは意味のある情報か否かが紛らわしいと思ったのでマスクしました、読み替えてください
- データベース名の指定が無いのは多分未指定だと
(default)
として扱われてるんじゃないかと思います(調べてないですごめんなさい) - コレクション名はこの手順で作成した
myCollection
を指定してるのでこれも適宜読み替えてください
import functions_framework
from flask import jsonify
from google.cloud import firestore
from werkzeug.exceptions import MethodNotAllowed
db_ref = firestore.Client(project='********') # MASKED
collection_ref = db_ref.collection('myCollection')
@functions_framework.http
def api(request):
"""Firestore CRUD Function.
"""
if request.method == 'POST': # Create
_, doc_ref = collection_ref.add(request.get_json())
return jsonify({'id': doc_ref.id}), 201
elif request.method == 'GET': # Read
docs = [{d.id: d.to_dict()} for d in collection_ref.stream()]
return jsonify(docs)
elif request.method == 'PUT': # Update
params = request.get_json()
doc_id = params.pop('id')
collection_ref.document(doc_id).update(params)
return jsonify({}), 204
elif request.method == 'DELETE': # Delete
collection_ref.document(request.get_json().get('id')).delete()
return jsonify({}), 204
# Other methods are not allowed
return jsonify({'message': MethodNotAllowed.description}), MethodNotAllowed.code
今回は単一のエンドポイントでCRUD処理を実装することを目的としています。
実際のバックエンド開発の場面では PUT
と DELETE
はパスで id
を指定してリソースの階層と一致させたいところです。
functions-framework==3.*
google-cloud-firestore
- Cloud Run Functions はFlaskアプリケーションとして動作しているため
Flask
パッケージの指定は不要 -
Werkzweug
パッケージはFlask
パッケージが依存しているため指定は不要
これをデプロイしたらシンプルなCRUD処理を行う Web API の完成です。
動作確認
- リクエスト先はマスクしているので自身の Cloud Run Functions のURLを適用してください
- パスの
/api
は関数のエントリポイントなのでこれも適宜変更
まずはCreateしてきます。
$ curl -X POST -H "Content-Type: application/json" -d '{"title":"foo"}' https://********.********.run.app/api
{"id":"nzNWqien4pzAjM72LY30"}
ちゃんと作成されていました。
リクエストパラメータやレスポンス( id
)と一致してます。
次はRead。
これも上記のスクショと一致してるのでOK。
$ curl -X GET https://********.********.run.app/api
[{"FirstDoc":{"foobar":"bazqux"}},{"nzNWqien4pzAjM72LY30":{"title":"foo"}}]
3つめはUpdate。
Createで作成したドキュメントの title
を更新します。
$ curl -X PUT -H "Content-Type: application/json" -d '{"id":"nzNWqien4pzAjM72LY30","title":"bar"}' https://********.********.run.app/api
ちゃんと更新されました。
最後はDelete。
作成、更新したドキュメントを削除します。
$ curl -X DELETE -H "Content-Type: application/json" -d '{"id":"nzNWqien4pzAjM72LY30"}' https://********.********.run.app/api
消えました。
おまけでそれ以外のメソッド。
ちゃんとエラーメッセージが応答してます。
$ curl -X OPTIONS https://********.********.run.app/api
{"message":"The method is not allowed for the requested URL."}
おしまい
触った所感、簡単なモックアップとか作るときにバックエンドとデータベースが必要な場面で力を発揮しそうです。無料枠で使えるし。
あと今回の実装の過程で google-cloud-firestore
パッケージのソースコードを結構読み込んだので、コードリーディングと各クラスで使えるメソッドもそのうち記事にまとめようと思います。(「思います」なんで多分思うだけで終わります)
さいごに、画面キャプチャでGCPプロジェクトのIDが見えてたり関数のURLが見えてたりしてますが、この記事が世に出ることにはいずれも抹消しているのでマスクせず公開してます。
みなさん情報管理には十分気をつけてくださいね。