├── .gitignore ├── requirements.txt ├── start.sh ├── Dockerfile ├── cloudbuild.yaml ├── README.md ├── nginx.conf └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | key.json 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | firebase_admin 3 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | service nginx start 3 | uwsgi --ini uwsgi.ini 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-stretch 2 | RUN apt-get update -y 3 | RUN apt-get install -y python-pip python-dev build-essential nginx 4 | COPY . /srv/flask_app 5 | WORKDIR /srv/flask_app 6 | RUN pip install -r requirements.txt --src /usr/local/src 7 | COPY nginx.conf /etc/nginx 8 | RUN chmod +x ./start.sh 9 | CMD ["./start.sh"] 10 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | # build & push the container image 3 | - name: "gcr.io/$PROJECT_ID/executor:latest" 4 | args: ["--cache=true", "--cache-ttl=48h", "--destination=gcr.io/fullstackgcp/todo:latest"] 5 | # Deploy container image to Cloud Run 6 | - name: "gcr.io/cloud-builders/gcloud" 7 | args: ['beta', 'run', 'deploy', 'todo', '--image', 'gcr.io/fullstackgcp/todo:latest', '--region', 'us-central1', '--allow-unauthenticated', '--platform', 'managed'] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 2 | 3 | # flask-firestore 4 | Building a Flask(Python) CRUD API with Firestore(Firebase) and Deploying on Cloud Run. 5 | 6 | 7 | 8 | ## Endpoints 9 | - `/add` - Create new document | POST 10 | - `/list` - List all documents | GET 11 | - `/update?id=ID_GOES_HERE` - Update a document | POST 12 | - `/delete?id=ID_GOES_HERE` - Delete a document | DELETE 13 | 14 | > Pass `?document_id=mydoc` to use a custom document 15 | > Pass `?collection=appointment` to use a custom collection. 16 | > Pass `?sub_collection=property` to use a custom sub_collection. 17 | 18 | ## Running Application 19 | ``` 20 | FLASK_APP=main.py FLASK_ENV=development flask run --port 8080 21 | or 22 | python3 main.py 23 | ``` 24 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 1024; 7 | use epoll; 8 | multi_accept on; 9 | } 10 | http { 11 | access_log /dev/stdout; 12 | error_log /dev/stdout; 13 | sendfile on; 14 | tcp_nopush on; 15 | tcp_nodelay on; 16 | keepalive_timeout 65; 17 | types_hash_max_size 2048; 18 | include /etc/nginx/mime.types; 19 | default_type application/octet-stream; 20 | index index.html index.htm; 21 | server { 22 | listen 8080 default_server; 23 | listen [::]:8080 default_server; 24 | server_name localhost; 25 | root /var/www/html; 26 | location / { 27 | include uwsgi_params; 28 | uwsgi_pass unix:/tmp/uwsgi.socket; 29 | uwsgi_read_timeout 1h; 30 | uwsgi_send_timeout 1h; 31 | proxy_send_timeout 1h; 32 | proxy_read_timeout 1h; 33 | } 34 | } 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Required Imports 2 | import os 3 | from flask import Flask, request, jsonify 4 | from firebase_admin import credentials, firestore, initialize_app 5 | 6 | # Initialize Flask App 7 | app = Flask(__name__) 8 | 9 | # Initialize Firestore DB 10 | cred = credentials.Certificate('key.json') 11 | default_app = initialize_app(cred) 12 | db = firestore.client() 13 | todo_ref = db.collection('todos') 14 | 15 | 16 | @app.route('/add', methods=['POST']) 17 | def create(): 18 | """ 19 | create() : Add document to Firestore collection with request body 20 | Ensure you pass a custom ID as part of json body in post request 21 | e.g. json={'id': '1', 'title': 'Write a blog post'} 22 | """ 23 | try: 24 | id = request.json['id'] 25 | todo_ref.document(id).set(request.json) 26 | return jsonify({"success": True}), 200 27 | except Exception as e: 28 | return f"An Error Occured: {e}" 29 | 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 | """ 39 | try: 40 | # Check if ID was passed to URL query 41 | todo_id = request.args.get('id') 42 | if todo_id: 43 | todo = todo_ref.document(todo_id).get() 44 | return jsonify(todo.to_dict()), 200 45 | else: 46 | all_todos = [doc.to_dict() for doc in todo_ref.stream()] 47 | return jsonify(all_todos), 200 48 | except Exception as e: 49 | return f"An Error Occured: {e}" 50 | 51 | 52 | @app.route('/update', methods=['POST', 'PUT']) 53 | def update(): 54 | """ 55 | update() : Update document in Firestore collection with request body 56 | Ensure you pass a custom ID as part of json body in post request 57 | e.g. json={'id': '1', 'title': 'Write a blog post today'} 58 | """ 59 | try: 60 | id = request.json['id'] 61 | todo_ref.document(id).update(request.json) 62 | return jsonify({"success": True}), 200 63 | except Exception as e: 64 | return f"An Error Occured: {e}" 65 | 66 | 67 | @app.route('/delete', methods=['GET', 'DELETE']) 68 | def delete(): 69 | """ 70 | delete() : Delete a document from Firestore collection 71 | 72 | """ 73 | try: 74 | # Check for ID in URL query 75 | todo_id = request.args.get('id') 76 | todo_ref.document(todo_id).delete() 77 | return jsonify({"success": True}), 200 78 | except Exception as e: 79 | return f"An Error Occured: {e}" 80 | 81 | 82 | port = int(os.environ.get('PORT', 8080)) 83 | if __name__ == '__main__': 84 | app.run(threaded=True, host='0.0.0.0', port=port) --------------------------------------------------------------------------------