Powered By GitBook
building-flask-api-with-cloud-firestore-and-deploying-to-cloud-run
Build a CRUD (create, read, update, delete) API to manage to-do lists using Flask (a microframework for Python) and Firestore, and deploy with Cloud Run.
Contributed by the Google Cloud community. Not official Google documentation.
In this tutorial, you build a CRUD (create, read, update, delete) API to manage to-do lists using Flask (a microframework for Python) and Firestore (a flexible, scalable database for mobile, web, and server development), and you deploy the API to Cloud Run (a serverless environment to run containers on Google Cloud).
​Firestore stores data as collections of documents. It also features richer, faster queries and scales further than the Firebase Realtime Database. You can manage to-do list fields through the API.

Requirements

Before you begin

    1.
    ​Create a new Firebase project, or use an existing one. 1. Click Database and Create database in the Cloud Firestore section. 1. Set your security rules and location.
    You should have an initial screen similar to the following:
    ​
    2.
    Download your Firebase Service Account Key. 1. Click the Settings icon at the top of the dashboard. 1. Click the Service Account tab. 1. Select Python option for Admin SDK configuration snippet, click Generate new private key, and save it as key.json.
    ​
    3.
    ​Create a new Google Cloud project, or use an existing one. You need the Google Cloud project so that you can deploy to Cloud Run.
    4.
    Open Cloud Shell or install the Cloud SDK.
    5.
    (Optional) To set up continuous deployment follow the instructions
    ​here.
    6.
    Ensure that you can run gcloud -h on in Cloud Shell.
    ​

Source code

1
# app.py
2
​
3
# Required imports
4
import os
5
from flask import Flask, request, jsonify
6
from firebase_admin import credentials, firestore, initialize_app
7
​
8
# Initialize Flask app
9
app = Flask(__name__)
10
​
11
# Initialize Firestore DB
12
cred = credentials.Certificate('key.json')
13
default_app = initialize_app(cred)
14
db = firestore.client()
15
todo_ref = db.collection('todos')
16
​
17
@app.route('/add', methods=['POST'])
18
def create():
19
"""
20
create() : Add document to Firestore collection with request body.
21
Ensure you pass a custom ID as part of json body in post request,
22
e.g. json={'id': '1', 'title': 'Write a blog post'}
23
"""
24
try:
25
id = request.json['id']
26
todo_ref.document(id).set(request.json)
27
return jsonify({"success": True}), 200
28
except Exception as e:
29
return f"An Error Occured: {e}"
30
​
31
@app.route('/list', methods=['GET'])
32
def read():
33
"""
34
read() : Fetches documents from Firestore collection as JSON.
35
todo : Return document that matches query ID.
36
all_todos : Return all documents.
37
"""
38
try:
39
# Check if ID was passed to URL query
40
todo_id = request.args.get('id')
41
if todo_id:
42
todo = todo_ref.document(todo_id).get()
43
return jsonify(todo.to_dict()), 200
44
else:
45
all_todos = [doc.to_dict() for doc in todo_ref.stream()]
46
return jsonify(all_todos), 200
47
except Exception as e:
48
return f"An Error Occured: {e}"
49
​
50
@app.route('/update', methods=['POST', 'PUT'])
51
def update():
52
"""
53
update() : Update document in Firestore collection with request body.
54
Ensure you pass a custom ID as part of json body in post request,
55
e.g. json={'id': '1', 'title': 'Write a blog post today'}
56
"""
57
try:
58
id = request.json['id']
59
todo_ref.document(id).update(request.json)
60
return jsonify({"success": True}), 200
61
except Exception as e:
62
return f"An Error Occured: {e}"
63
​
64
@app.route('/delete', methods=['GET', 'DELETE'])
65
def delete():
66
"""
67
delete() : Delete a document from Firestore collection.
68
"""
69
try:
70
# Check for ID in URL query
71
todo_id = request.args.get('id')
72
todo_ref.document(todo_id).delete()
73
return jsonify({"success": True}), 200
74
except Exception as e:
75
return f"An Error Occured: {e}"
76
​
77
port = int(os.environ.get('PORT', 8080))
78
if __name__ == '__main__':
79
app.run(threaded=True, host='0.0.0.0', port=port)
Copied!
There are individual methods and routes for each action that the API performs. You can improve upon the code snippet and add more functions to meet your needs.
For each CRUD (create, read, update, delete) action, you define the route and its corresponding HTTP method. The API implementation tries to perform that action and returns a response with the 200 status code if successful. If there's a problem, the implementation returns the exception's error code.

Deploy to Cloud Run

To build your API implementation in a container and run it on Cloud Run, you need a Dockerfile:
1
# Dockerfile
2
FROM python:3.7-stretch
3
RUN apt-get update -y
4
RUN apt-get install -y python-pip python-dev build-essential
5
COPY . /app
6
WORKDIR /app
7
RUN pip install -r requirements.txt
8
ENTRYPOINT ["python"]
9
CMD ["app.py"]
Copied!
Ensure that you have a requirements.txt file with the following contents:
1
# requirements.txt
2
flask
3
firebase_admin
Copied!
Finally, create a cloudbuild.yaml file, which you will use to trigger builds:
1
# cloudbuild.yaml
2
steps:
3
# build & push the container image
4
- name: "gcr.io/kaniko-project/executor:latest"
5
args: ["--cache=true", "--cache-ttl=48h", "--destination=gcr.io/$PROJECT_ID/todo:latest"]
6
# Deploy container image to Cloud Run
7
- name: "gcr.io/cloud-builders/gcloud"
8
args: ['beta', 'run', 'deploy', 'todo', '--image', 'gcr.io/$PROJECT_ID/todo:latest', '--region', 'us-central1', '--allow-unauthenticated', '--platform', 'managed']
Copied!
You can also import your key.json Firebase service account file into the same directory; it is recommended that you not push it to your source repository. A fast approach to this is to add an additional Cloud Build step that downloads the service account from a private location, such as a Cloud Storage bucket.

Execute the build and deploy steps with Cloud Shell

Run the following command to build your Docker container and push to Container Registry as specified in the cloudbuild.yaml file.
1
gcloud builds submit --config cloudbuild.yaml .
Copied!
This also performs the step of deploying to Cloud Run.

(Optional) Deploy using the Cloud Run Button

Recently, Google announced Cloud Run Button, an image and link that you can add to the README file for your source code repositories to allow others to deploy your application to Google Cloud using Cloud Run.
The steps to add the Cloud Run Button to your repository are as follows:
    1.
    Copy and paste this Markdown into your README.md file:
    1
    [![Run on Google Cloud](https://storage.googleapis.com/cloudrun/button.svg)](https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=[YOUR_HTTP_GIT_URL])
    Copied!
    2.
    Replace [YOUR_HTTP_GIT_URL] with your HTTP git URL, as in the following example:
    1
    [![Run on Google Cloud](https://storage.googleapis.com/cloudrun/button.svg)](https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=https://github.com/GoogleCloudPlatform/flask-firestore.git)
    Copied!
    This creates a button like the following:
    ​​
    ​
    ​
    3.
    Ensure that your repository has a Dockerfile.

Cleaning up

To prevent unnecessary charges, clean up the resources created for this tutorial. This includes deleting any projects that you created for this tutorial.

Useful links

Last modified 7mo ago