{"id":18968666,"url":"https://github.com/r100-stack/flask-firebase-chat","last_synced_at":"2025-04-19T14:45:26.159Z","repository":{"id":120424601,"uuid":"313819056","full_name":"r100-stack/Flask-Firebase-Chat","owner":"r100-stack","description":"Exercise to demonstrate possibilities of using (Flask + MySQL) or (Firestore) backends","archived":false,"fork":false,"pushed_at":"2020-12-23T18:00:24.000Z","size":8267,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-16T08:11:10.578Z","etag":null,"topics":["firebase","firebase-hosting","firestore","flask","linux-vm","mysql","mysql-connector"],"latest_commit_sha":null,"homepage":"https://flaskchat.rohankadkol.com","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/r100-stack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-18T04:05:18.000Z","updated_at":"2024-02-23T13:09:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"33789a7d-18e9-402a-b73c-86cf5463ad02","html_url":"https://github.com/r100-stack/Flask-Firebase-Chat","commit_stats":null,"previous_names":["r100-stack/flask-firebase-chat"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r100-stack%2FFlask-Firebase-Chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r100-stack%2FFlask-Firebase-Chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r100-stack%2FFlask-Firebase-Chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r100-stack%2FFlask-Firebase-Chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/r100-stack","download_url":"https://codeload.github.com/r100-stack/Flask-Firebase-Chat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249716918,"owners_count":21315068,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["firebase","firebase-hosting","firestore","flask","linux-vm","mysql","mysql-connector"],"created_at":"2024-11-08T14:48:14.052Z","updated_at":"2025-04-19T14:45:26.153Z","avatar_url":"https://github.com/r100-stack.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flask Firebase Chat\n\n## Table of contents\n\n- [Flask Firebase Chat](#flask-firebase-chat)\n  * [Table of contents](#table-of-contents)\n  * [How to follow this repo](#how-to-follow-this-repo)\n  * [How to use git in this repo](#how-to-use-git-in-this-repo)\n  * [Setup to follow this repo](#setup-to-follow-this-repo)\n    + [Option 1 (**Recommended**)](#option-1-(**recommended**))\n    + [Option 2 (Manual setup)](#option-2-(manual-setup))\n  * [Setting up Python dependencies](#setting-up-python-dependencies)\n  * [Cloning repo](#cloning-repo)\n- [1. Flask version](#1-flask-version)\n  * [Step 1: Setting up and starting the Flask server](#step-1-setting-up-and-starting-the-flask-server)\n  * [Step 2: Creating a connection to the MySQL db](#step-2-creating-a-connection-to-the-mysql-db)\n  * [Step 3: GET /messages](#step-3-get-messages)\n  * [Step 4: POST /messages](#step-4-post-messages)\n  * [Step 5: DELETE /messages](#step-5-delete-messages)\n  * [Step 6: PATCH /messages](#step-6-patch-messages)\n- [3. Firebase Version](#3-firebase-version)\n  * [Firebase setup](#firebase-setup)\n    + [Option 1: Use pre-setup Firebase config (quicker)](#option-1-use-pre-setup-firebase-config-quicker-)\n    + [Option 2: Setup a new Firebase project (required for step 11: Firebase Hosting)](#option-2-setup-a-new-firebase-project-required-for-step-11-firebase-hosting-)\n  * [Step 7: Firebase setup and GET messages](#step-7-firebase-setup-and-get-messages)\n  * [Step 8: Sending a message](#step-8-sending-a-message)\n  * [Step 9: Deleting a message](#step-9-deleting-a-message)\n  * [Step 10: Editing a message](#step-10-editing-a-message)\n- [4. Firebase Hosting](#4-firebase-hosting)\n  * [Step 11: Hosting the Firebase version of the website with Firebase Hosting](#step-11-hosting-the-firebase-version-of-the-website-with-firebase-hosting)\n- [Conclusion](#conclusion)\n\nToday, almost all websites/apps provide dynamic content. This dynamic content could be inventory of products on sale, list of customers, orders, and more.\n\nTo maintain and provide this dynamic content, we need a database layer of some sort. To access this database layer easily, we would also need a backend layer as an abstraction.\n\nDifferent database and backend solutions exist in the market today. In this exercise, we are going to talk about:\n\n1. Flask (backend server) + MySQL (database server)\n2. Firebase's Firestore (serverless backend and database)\n\nYou can see a demo of the finished exercise on [flaskchat.rohankadkol.com](https://flaskchat.rohankadkol.com)\n\nHere is a screeshot of what we're going to build.\n\n\u003cimg src=\"assets/flask_screenshot_1.gif\"\u003e\n\n## How to follow this repo\n\nThis repo is an exercise that is split into 10 steps. Each step is further split into starter and solution code branches. Each branch has a couple of ``TODOs``.\n\nThe recommended way to follow this repo is to complete each of the 10 steps in order. Also, it's recommended to complete the ``TODOs`` in each step in order.\n\nThe solutions to each ``TODO`` is provided in this ``README``. The solutions for each step is also available in the ``SolutionCode`` branch of that step. Eg. ``Step1-StarterCode`` contains all the incomplete TODOs for step 1 and ``Step1-SolutionCode`` contains the solution for each TODO in step 1.\n\n## How to use git in this repo\n\nTo switch between branches, you can use the ``git``. You can either use the branch selector on the bottom left of VSCode or you can use some ``git`` commands in the terminal.\n\n```bash\n# To clone to repo\ngit clone https://github.com/rohan-kadkol/Flask-Firebase-Chat.git\n\n# To list all branches\ngit branch -a\n\n# To checkout a branch. Note, the branch will be downloaded if it doesn't exist locally\ngit checkout \u003cbranch name\u003e\nexample,\ngit checkout Step1-StarterCode\n\n# Save your work in a branch (This may be needed before switching to a new branch)\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\n## Setup to follow this repo\n\nWe will be using MySQL as our backend. Hence, let us set it up for our app.\n\nRequirements:\n\n1. MySQL server\n2. Credentials to access the MySQL server\n\n### Option 1 (**Recommended**)\n\nTo save time and avoid setup errors, you can use this [pre-setup VM](https://drive.google.com/drive/folders/1AN_HY_vOSVv108QnYIhSHkFDQ-Z8mmYK?usp=sharing).\n\n* VM login: ``kali``\n* VM password: ``kali``\n* MySQL username: ``user``\n* MySQL password: ``password``\n\nWhat does the VM contain?\n\n* VSCode\n    * Todo Tree extension\n    * Python extension\n    * and more...\n* MySQL server\n    * Running server\n    * Credentials\n* All required apt-get packages\n* Firebase command line tool\n\n### Option 2 (Manual setup)\n\nFollow the below instructions on either your local or virtual machine. Choose the appropriate option depending on your OS.\n\n* Windows: [Tutorial guide](https://www.liquidweb.com/kb/install-mysql-windows/)\n* Linux: [Tutorial guide](https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04)\n* Mac: [Tutorial guide](https://www.positronx.io/how-to-install-mysql-on-mac-configure-mysql-in-terminal/)\n\n___\n\nTo test if your MySQL server is working, type ``mysql`` in the command line. You might get an error similar to the one below\n\n```\nERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)\n```\n\nIf you do get this error, you can start the server with this command\n\n```bash\n/etc/init.d/mysql start\n```\n\nIf it asks for a password, enter the system password (``kali`` if you're using the [pre-setup VM](###option-1-(**recommended**)))\n\n___\n\nOnce you have a **running** MySQL server along with **valid credentials**, we can setup our messages table in the database.\n\nRun the below commands to setup the table.\n\n```\nmysql -u user -p\n\u003cEnter your MySQL password\u003e\n```\n\n```\ncreate database flaskchat;\n\nuse flaskchat;\n\ncreate table messages (\n    ID int auto_increment,\n    message varchar(200),\n    sender varchar(50),\n    primary key (ID)\n);\n```\n\n## Setting up Python dependencies\n\nRegardless of where you're using the pre-setup VM or your local machine, we need to setup some Python dependencies.\n\nInstead of installing these dependencies on your local system's Python installation, we will install it in a Python virtual environment. This increases modularity, prevents dependency version conflicts, and more.\n\n```bash\n# To create a virtual environment\npython3 -m venv env\n\n# To enter the created virtual environment\nsource env/bin/activate\n\n# To exit the virtual environment\ndeactivate\n```\n\nIf you see an (env) in the beginning of each command, you have successfully created and entered the Python virtual environment.\n\n\u003cimg src=\"assets/python_venv_setup_editted.png\"\u003e\n\nAll dependencies we need for this project are in ``requirements.txt``. To install it, run the following in the terminal **while entered in the Python virtual environment**.\n\n```bash\npip install -r requirements.txt\n```\n\n## Cloning repo\n\n1. Go to the Documents folder (/home/kali/Documents).\n2. Next to clone the repo, execute the following command.\n\n```bash\ngit clone https://github.com/rohan-kadkol/Flask-Firebase-Chat.git\n```\n\n\n# 1. Flask version\n\n## Step 1: Setting up and starting the Flask server\n\nTo begin working on this step, switch to the ``Step1-StarterCode`` branch.\n\n```bash\ngit checkout Step1-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step1-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_1.png\"\u003e\n\n___\n\n\nFlask makes server setup very easy. With just a few lines of code, we can get a basic server up and running.\n\nLet's start by doing some imports.\n\n``init_flask.py``\n```python\n# TODO 1-2\nfrom flask import Flask\nfrom sqlalchemy import create_engine\n\n# TODO 3\nimport os\n```\n\nInstead of hard coding our database's credentials, let us take it as an environment variable. Then the credentials can be dynamically entered through the command line. This increases flexibility and also more importantly increases security.\n\n``init_flask.py``\n```python\n# TODO 4-5\nusername = os.environ['FLASKCHAT_DB_USERNAME']\npassword = os.environ['FLASKCHAT_DB_PASSWORD']\n```\n\nNow, let us create a definition of the Flask app. Here we can specify additional configurations for the Flask app. However, in our basic case, no additional configurations are needed.\n\n``init_flask.py``\n```python\n# TODO 6\napp = Flask(__name__)\n```\n\nTo link our Flask server with our MySQL server, we need to create a URI that refers to the database. Then we need to create a database engine using this URI.\n\n``init_flask.py``\n```python\n# TODO 7-8\nDATABSE_URI='mysql+mysqlconnector://{user}:{password}@{server}/{database}'.format(user=username, password=password, server='localhost', database='flaskchat')\n\nengine = create_engine(DATABSE_URI)\n```\n\nThat is it for the Flask server setup! Now let's start working on our first endpoint.\n\nOur first endpoint will be ``GET /``. This endpoint should return the ``index.html`` file in the ``/templates`` folder. The ``index.html`` file is in the ``/templates`` folder as Flask, by default, looks for ``html`` files in the ``/templates`` folder. Keeping it in any other folder and refering to it will give an error.\n\n``app.py``\n```python\n# TODO 9-10\nfrom flask import Flask, render_template\nfrom init_flask import app\n\n# TODO 11-13\n@app.route('/', methods=['GET'])\ndef index():\n    \"\"\"GET / - Endpoint to display the homepage HTML file.\"\"\"\n\n    return render_template('index.html')\n```\n\nNow to run the server, head to the terminal and cd into the project directory. Next, let us setup some environment variables needed to configure Flask. Finally, we can run a simple command to start the Flask server.\n\n```bash\n# Required: To tell which is the main Flask file with all endpoints\nexport FLASK_APP=app.py\n\n# Optional: To tell that this Flask server is not in development (non-production) mode. Hence, it will automatically reload the server when it detects changes.\nexport FLASK_ENV=development\n\n# Required: MySQL db credentials used by init_flask.py\n# MySQL db username\nexport FLASKCHAT_DB_USERNAME=user\n# MySQL db password\nexport FLASKCHAT_DB_PASSWORD=password\n\n# To start the Flask server\nflask run\n```\n\nTo test the server, open a browser and go to [localhost:5000](localhost:5000). You should now see an HTML page that says Flask Chat. Don't worry if you get an error saying \"Error downloading messages\". We will fix that in the later steps.\n\n\u003cimg src=\"assets/step1_sol.png\"\u003e\n\n## Step 2: Creating a connection to the MySQL db\n\nTo begin working on this step, switch to the ``Step2-StarterCode`` branch.\n\n```bash\ngit checkout Step2-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step2-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_2.png\"\u003e\n\n___\n\n\nProgramming is not only about getting the right output. It is also about robustness, efficiency, code structure and more. A common programming pattern to avoid bugs and improve readability is to create layers of abstractions. We're going to do the same.\n\nLet us create a layer of abstraction that creates a database connection. This connection can then be used by any endpoint we implement in the future steps.\n\n``error_handling.py``\n```python\n# TODO 1-3\nconn = engine.connect()\nresponse = func(conn=conn)\n```\n\nWhen there is no error, the try block executes completely. Hence, ``response = output of func``. However, when there is an error, we should return an HTTP response with code 500.\n\n``error_handling.py``\n```python\n# TODO 4-5\nresponse = jsonify({\n    'success': False,\n    'error': str(ex)\n}), 500\n```\n\nDatabase connections must always be closed after its use. Reusing database connections and/or unclosed connections may cause problems down the line.\n\nAfter closing the connection, we can return the response generated by either the try block (success, 200) or the catch block (failure, 500)\n\n``error_handling.py``\n```python\n# TODO 6\nconn.close()\n\n# TODO 7\nreturn response\n```\n\nThis step was just a layer of abstraction. Hence, there won't be any visible change in website.\n\n```bash\n# To start the Flask server\nflask run\n```\n\n\u003cimg src=\"assets/step2_sol.png\"\u003e\n\n## Step 3: GET /messages\n\nTo begin working on this step, switch to the ``Step3-StarterCode`` branch.\n\n```bash\ngit checkout Step3-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step3-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_3.png\"\u003e\n\n___\n\n\nTime to create our second endpoint! We will now create a ``GET /messages`` endpoint to return all the messages in our db.\n\nFirst let us do some imports.\n\n``app.py``\n```python\n# TODO 1\nfrom flask import Flask, jsonify, render_template\n\n# TODO 2\nfrom error_handling import get_connection_and_handle_error\n```\n\nNow, let us create the endpoint similar to step 1 (``GET /``).\n\nLet us also include the ``@get_connection_and_handle_error`` decorator (layer of abstraction) that we created in step 2. This will give us a db connection that we can use to run SQL statements.\n\nDefine a function ``get_messages(*args, **kwargs)``.\n\n``app.py``\n```python\n# TODO 3-5\n@app.route('/messages', methods=['GET'])\n@get_connection_and_handle_error\ndef get_messages(*args, **kwargs):\n```\n\nThen use ``**kwargs`` to get the db connection that ``@get_connection_and_handle_error`` provides.\n\nUsing the db conn, execute the following SQL statement.\n```sql\nselect ID, message, sender from messages order by ID desc;\n```\n\nIterate through all the records the above SQL statement returns to create a list of messages.\n\nFinally, return an HTTP 200 response with ``success=True`` and the list of messages\n\n``app.py``\n```python\n# TODO 6-10\n@app.route('/messages', methods=['GET'])\n@get_connection_and_handle_error\ndef get_messages(*args, **kwargs):\n    \"\"\"GET /messages - Returns a JSON response with the messages in the database.\"\"\"\n\n    conn = kwargs['conn']\n    results = conn.execute(\n        'select ID, message, sender from messages order by ID desc;')\n\n    messages = []\n\n    for row in results:\n        messages.append({\n            'ID': row['ID'],\n            'message': row['message'],\n            'sender': row['sender']\n        })\n\n    return jsonify({\n        'success': True,\n        'messages': messages\n    })\n```\n\nNow let's configure our frontend to make a request to the ``GET /messages`` endpoint of our backend.\n\n``custom_ajax_flask.js``\n```javascript\n// TODO 11-16\n$.ajax({\n    url: url,\n    type: type,\n    headers: { 'Content-Type': 'application/json' },\n    data: JSON.stringify(body),\n    complete: function (response) {\n        console.log(response.responseJSON);\n        resolve(response.responseJSON);\n    }\n});\n\n// TODO 17-18\nresponse = await sendAjaxRequest('/messages', 'GET', null);\n    return response;\n```\n\nSometimes a regular refresh using ``F5`` may not display any changes. Hence, do a hard refresh on your browser by pressing ``Ctrl+F5``.\n\nYou should now see some samples messages or no messages if no sample messages exist in the db. Regardless, you shouldn't see the error that we were seeing in steps 1 and 2.\n\n```bash\n# To start the Flask server\nflask run\n```\n\n\u003cimg src=\"assets/step3_sol.gif\"\u003e\n\n## Step 4: POST /messages\n\nTo begin working on this step, switch to the ``Step4-StarterCode`` branch.\n\n```bash\ngit checkout Step4-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step4-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_4.png\"\u003e\n\n___\n\n\nThere's no fun in just seeing sample messages. Let us work on adding our own messages to the database.\n\nLet us create a ``POST /messages`` endpoint (Similar to step 3), and use the ``@get_connection_and_handle_error`` to get a db connection.\n\nThe only difference is that our frontend now will send a the message and message sender along with the request. Hence, use ``request.json['key']`` to get the ``'message'`` and ``'sender'``.\n\nExecute the following sql statement\n```sql\ninsert into messages (message, sender) values (%s, %s);\n```\n\nFinally, return an HTTP 200 response with ``success=True``\n\n``app.py``\n```python\n# TODO 1\nfrom flask import Flask, jsonify, render_template, request\n\n# TODO 2-8\n@app.route('/messages', methods=['POST'])\n@get_connection_and_handle_error\ndef add_message(*args, **kwargs):\n    \"\"\"POST /messages - Adds a message to the database.\"\"\"\n\n    conn = kwargs['conn']\n\n    message = request.json['message']\n    sender = request.json['sender']\n\n    conn.execute(\n        'insert into messages (message, sender) values (%s, %s);', (message, sender,))\n\n    return jsonify({\n        'success': True\n    })\n```\n\nNow let's configure our frontend to make a request to the ``POST /messages`` endpoint of our backend.\n\n``custom_ajax_flask.js``\n```javascript\n// TODO 9-10\nresponse = await sendAjaxRequest('/messages', 'POST', {\n    'message': message,\n    'sender': sender\n});\nreturn response;\n```\n\nAfter doing a hard reload (``Ctrl+F5``), you can test adding a message. After clicking the ``Send`` button, you should see the message appear in the messages list.\n\n```bash\n# To start the Flask server\nflask run\n```\n\n\u003cimg src=\"assets/step4_sol.gif\"\u003e\n\n## Step 5: DELETE /messages\n\nTo begin working on this step, switch to the ``Step5-StarterCode`` branch.\n\n```bash\ngit checkout Step5-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step5-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_5.png\"\u003e\n\n___\n\n\n\"Oops, I sent a message by accident. I want to delete it.\"\n\nLet's now work on deleting a message.\n\nFirst, create a ``DELETE /messages`` endpoint, use the ``@get_connection_and_handle_error`` decorator, and extract the provided connection using ``**kwargs``.\n\nNext, use ``request`` to extract the ID of the message to delete.\n\nDelete the message using the following SQL statement.\n\n```sql\ndelete from messages where ID=%s;\n```\n\nFinally, return an HTTP 200 response with ``success=True``.\n\n``app.py``\n```python\n# TODO 1-7\n@app.route('/messages', methods=['DELETE'])\n@get_connection_and_handle_error\ndef delete_message(*args, **kwargs):\n    \"\"\"DELETE /messages - Deletes a message from the database.\"\"\"\n\n    conn = kwargs['conn']\n\n    ID = request.json['ID']\n\n    conn.execute('delete from messages where ID=%s;', (ID,))\n\n    return jsonify({\n        'success': True\n    })\n```\n\nNow let's configure our frontend to make a request to the ``DELETE /messages`` endpoint of our backend.\n\n``custom_ajax_flask.js``\n```javascript\n// TODO 8-9\nresponse = await sendAjaxRequest('/messages', 'DELETE', {\n    'ID': ID\n});\nreturn response;\n```\n\nNow clicking the trash button on each message will delete the message.\n\n```bash\n# To start the Flask server\nflask run\n```\n\n\u003cimg src=\"assets/step5_sol.gif\"\u003e\n\n## Step 6: PATCH /messages\n\nTo begin working on this step, switch to the ``Step6-StarterCode`` branch.\n\n```bash\ngit checkout Step6-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step6-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_6.png\"\u003e\n\n___\n\n\nMade an embarassing typo? Let's implement the edit functionality so we correct that embarassing typo.\n\nFirst, create a ``PATCH /messages`` endpoint, use the ``@get_connection_and_handle_error`` decorator, and extract the provided connection using ``**kwargs``.\n\nNext, use ``request`` to extract the ID and message of the message to edit.\n\nEdit the message using the following SQL statement.\n\n```sql\nupdate messages set message=%s where ID=%s;\n```\n\nFinally, return an HTTP 200 response with ``success=True``.\n\n``app.py``\n```python\n# TODO 1-7\n@app.route('/messages', methods=['PATCH'])\n@get_connection_and_handle_error\ndef edit_message(*args, **kwargs):\n    \"\"\"PATCH /messages - Edits a message in the database.\"\"\"\n\n    conn = kwargs['conn']\n\n    ID = request.json['ID']\n    message = request.json['message']\n\n    conn.execute('update messages set message=%s where ID=%s;', (message, ID,))\n\n    return jsonify({\n        'success': True\n    })\n```\n\nNow let's configure our frontend to make a request to the ``PATCH /messages`` endpoint of our backend.\n\n``custom_ajax_flask.js``\n```javascript\n// TODO 8-9\nresponse = await sendAjaxRequest('/messages', 'PATCH', {\n    'ID': ID,\n    'message': message\n});\nreturn response;\n```\n\nYou should now be able to edit messages.\n\n```bash\n# To start the Flask server\nflask run\n```\n\n\u003cimg src=\"assets/step6_sol.gif\"\u003e\n\n# 3. Firebase Version\n\nThe Flask version works well. It helps us read, add, delete, and update messages in the database. However, with the current implementation, there is one problem: the updates are not real-time.\n\nFor example, consider you have two clients using FlaskChat at the same time. When ``Client 1`` sends a message, ``Client 2`` doesn't see the new message instantly. ``Client 2`` only sees the new message on refreshing the page.\n\nWe can have the client send a ``GET /messages`` request every ~10 seconds. However, this is inefficient.\n\nWe can use some efficient methods like ``Web Sockets``, ``Streaming``, or some other similar methods to make our server real-time. However, these solutions might get complex. \n\nMaking a database/backend real-time is very common. Then a question arrises on why should we implement it every time? What if it could be made as boilerplate code?\n\n** Enters Firestore\n\n[Firestore](https://firebase.google.com/docs/firestore) is a feature provided by a service called [Firebase](https://firebase.google.com/). Firestore is a NoSQL database that provides real-time implementation with almost no setup.\n\nBenefits of Firestore:\n* Real-time implementation already done\n* Scalable: Firestore automatically scales depending on usage without any setup.\n* Serverless: Deploying this db requires no VMs, Kubernetes, etc.\n\nLooking at the benefits Firestore has to offer, we will now implement our getting, adding, deleting, and updating of messages using Firestore instead of Flask.\n\n## Firebase setup\n\n### Option 1: Use pre-setup Firebase config (quicker)\n\n**If you want to use Firebase hosting, you'll need your own Firebase project. Hence, follow option 2 below**\n\nTo save time, you can use the pre-setup configuration. We have created a Firebase project called ``Flask Chat`` with Project ID: ``flaskchat-6684a``.\n\nBelow is the ``var firebaseConfig`` needed to configure Firebase\n\n```javascript\nvar firebaseConfig = {\n    apiKey: \"AIzaSyAW2ELiIJpHrkU40So9iOqePeGBcgfOB-E\",\n    authDomain: \"flaskchat-6684a.firebaseapp.com\",\n    databaseURL: \"https://flaskchat-6684a.firebaseio.com\",\n    projectId: \"flaskchat-6684a\",\n    storageBucket: \"flaskchat-6684a.appspot.com\",\n    messagingSenderId: \"94841685089\",\n    appId: \"1:94841685089:web:f1abf658720a87403cd620\",\n    measurementId: \"G-FWENEM06PJ\"\n};\n```\n\n### Option 2: Setup a new Firebase project (required for step 11: Firebase Hosting)\n\nInstead of using the pre-setup Firebase project in option 1, you can setup your own Firebase project.\n\n1. Head to [console.firebase.google.com](https://console.firebase.google.com)\n2. Click on \"Add Project\" or something similar\n3. Enter a project name of your choice\n\u003cimg src=\"assets/firebase_create_project.png\"\u003e\n4. Choose whether you want to enable Firebase analytics\n\u003cimg src=\"assets/firebase_create_project_2.png\"\u003e\n5. Choose or create a Google Analytics account (only if Firebase analytics is chosen)\n\u003cimg src=\"assets/firebase_create_project_3.png\"\u003e\n6. Click on continue when your project setup is ready\n\u003cimg src=\"assets/firebase_create_project_4.png\"\u003e\n7. On the dashboard, click the ``\u003c/\u003e`` icon near ``Get started by adding Firebase to your app``. This will register a Firebase web app to the Firebase project\n\u003cimg src=\"assets/firebase_register_web_app.png\"\u003e\n8. Choose an app name of your liking\n\u003cimg src=\"assets/firebase_register_web_app_2.png\"\u003e\n9. Copy only the definition of ``var firebaseConfig``. This is the ``firebaseConfig`` variable that points to your Firebase Project.\n\u003cimg src=\"assets/firebase_register_web_app_3.png\"\u003e\n\nYour ``var firebaseConfig`` should look similar to the ``var firebaseConfig`` above.\n\n## Step 7: Firebase setup and GET messages\n\nTo begin working on this step, switch to the ``Step7-StarterCode`` branch.\n\n```bash\ngit checkout Step7-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step7-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_7.png\"\u003e\n\n___\n\n\nNow that we have ``var firebaseConfig`` pointing to either the pre-setup Firebase project or your own Firebase project, we can paste it in our project.\n\nBelow is the configuration code for that project. It contains:\n* Firebase Project config (``var firebaseConfig``) obtained from above\n* Firebase dependencies\n    * Firebase app\n    * Firebase analytics\n    * Firebase firestore\n* Initialize\n    * Firebase app\n    * Firebase analytics\n\n**Be sure to replace the below ``var firebaseConfig`` with your own if you are using your own Firebase Project and not the pre-setup one.**\n\nPaste the below code for TODO 1.\n\n``/frontend/index.html``\n```html\n\u003c!-- TODO 1 --\u003e\n\n\u003c!-- 4. All Firebase/Firestore dependencies --\u003e\n\u003c!-- The core Firebase JS SDK is always required and must be listed first --\u003e\n\u003cscript src=\"https://www.gstatic.com/firebasejs/8.0.2/firebase-app.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Dependency to analysize # visitors and other analytical data --\u003e\n\u003cscript src=\"https://www.gstatic.com/firebasejs/8.0.2/firebase-analytics.js\"\u003e\u003c/script\u003e\n\n\u003c!-- Dependency to use Firestore in your project --\u003e\n\u003cscript src=\"https://www.gstatic.com/firebasejs/8.0.2/firebase-firestore.js\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\n    // Your web app's Firebase configuration\n    // For Firebase JS SDK v7.20.0 and later, measurementId is optional\n    \n    // TODO: Replace with your own firebaseConfig if you are using your own Firebase Project and not the pre-setup one\n    \n    var firebaseConfig = {\n        apiKey: \"AIzaSyAW2ELiIJpHrkU40So9iOqePeGBcgfOB-E\",\n        authDomain: \"flaskchat-6684a.firebaseapp.com\",\n        databaseURL: \"https://flaskchat-6684a.firebaseio.com\",\n        projectId: \"flaskchat-6684a\",\n        storageBucket: \"flaskchat-6684a.appspot.com\",\n        messagingSenderId: \"94841685089\",\n        appId: \"1:94841685089:web:f1abf658720a87403cd620\",\n        measurementId: \"G-FWENEM06PJ\"\n    };\n    // Initialize Firebase\n    firebase.initializeApp(firebaseConfig);\n    firebase.analytics();\n\n    var db = firebase.firestore();\n\u003c/script\u003e\n\u003c!-- End of Firebase/Firestore dependencies --\u003e\n```\n___\n\nNow that Firebase is set up, we can work on getting the messages.\n\nNow, to get the messages, all we have to do is subscribe as a listener. We would also need to provide a callback to be executed when changes in the Firestore db are detected. This is what makes the Firestore db to be real-time.\n\nOn Firebase, data is stored in ``collections``. Each collection has ``documents``. Each document is a set of ``key-value pairs``. Apart from documents, collections can also hold more collections called ``sub-collections``. This enables Firestore to store data in a hierarchical fashion.\n\nWe are going to have a collection called `messages`. In this ``messages`` collection, each document will be a message. Each document (message) will have three fields: ``message``, ``sender``, and ``timestamp``.\n\nHere's an example of how our data will be stored:\n\u003cimg src=\"assets/firestore_structure_example.png\"\u003e\n\nNow that we have planned the structure of our Firestore db, let us work on getting the messages. \n\nFirst, use ``db`` and call ``.collection('messages')`` on it. To sort the messages by descending timestamps, simply call ``.orderBy('timestamp', 'desc')`` to the chain.\n\nFinally to subscribe as a listener and add a callback, add a ``.onSnapshot((querySnapshot) {})`` to the chain. Here, querySnapshot is a snapshot of all documents (messages) in the ``messages`` collection.\n\nWithin the curly braces of the callback function, create a list of messages by iterating through the ``querySnapshot``.\n\nThen call ``onChange(messages)`` and pass the list of messages as a parameter. Here, ``onChange(messages)`` is a frontend method that displays the passed list of messages to the UI.\n\n``custom_ajax_firebase.js``\n```javascript\n// TODO 2-4\nfunction sendGetMessages(onChange) {\n    db.collection(\"messages\").orderBy('timestamp', 'desc').onSnapshot((querySnapshot) =\u003e {\n        var messages = [];\n\n        querySnapshot.forEach((doc) =\u003e {\n            messages.push({\n                'ID': doc.id,\n                'message': doc.data()['message'],\n                'sender': doc.data()['sender']\n            });\n        });\n\n        onChange(messages);\n    });\n}\n```\n\nTo test the website, you will have to open the ``index.html`` file and not ``localhost:5000``. This is because in our new version, the javascript in the HTML page directly sends requests to the Firestore backend db. There is no intermidiate server as in the case of the Flask implementation.\n\nHence, type the path to the file in the browser. It should look something similar to the one below.\n\n```\nGo to /home/kali/Documents/Flask-Firebase-Chat/frontend/index.html\nUse Ctrl+F5 for hard reload if changes not appearing\n```\n\nYou should now be able to get messages.\n\n\u003cimg src=\"assets/step7_sol.gif\"\u003e\n\n## Step 8: Sending a message\n\nTo begin working on this step, switch to the ``Step8-StarterCode`` branch.\n\n```bash\ngit checkout Step8-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step8-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_8.png\"\u003e\n\n___\n\n\nAdding a document to the Firestore db is as easy as doing a ``.add()`` call to the ``db.collection()`` and passing the JSON object to add as a parameter to ``.add()``.\n\nWe can use ``.then(function() {})`` and ``.catch(function(error) {})`` to execute code on success and on failure respectively.\n\nAdditionally, we wrap the ``.add()`` call in a ``Promise``. We do this so the frontend calling this method can use ``await`` to halt execution until a response is received.\n\n``custom_ajax_firebase.js``\n```javascript\n// TODO 1-6\nasync function sendPostMessage(message, sender) {\n    return new Promise(async resolve =\u003e {\n        await db.collection(\"messages\").add({\n            message: message,\n            sender: sender,\n            timestamp: firebase.firestore.Timestamp.now()\n        })\n            .then(function () {\n                console.log('insert successful');\n                resolve({ 'success': true });\n            })\n            .catch(function (error) {\n                console.log(error);\n                resolve({ 'success': false });\n            });\n    });\n}\n```\n\n```\nGo to /home/kali/Documents/Flask-Firebase-Chat/frontend/index.html\nUse Ctrl+F5 for hard reload if changes not appearing\n```\n\nYou should now be able to get and add messages.\n\n\u003cimg src=\"assets/step8_sol.gif\"\u003e\n\n## Step 9: Deleting a message\n\nTo begin working on this step, switch to the ``Step9-StarterCode`` branch.\n\n```bash\ngit checkout Step9-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step9-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_9.png\"\u003e\n\n___\n\n\nTo delete a document, we first need a reference to it. Each document is given a unique ID by Firebase. Hence, to get a reference to a document, we can do ``db.collection('messages').doc(ID)``, where ID is the id of the document.\n\nOnce we have a reference to the document, we can call a ``.delete()`` on it.\n\nAdditionally, we use a ``.then(function() {})`` and ``.catch(function(error) {})`` to execute code on success and on failure respectively.\n\n``custom_ajax_firebase.js``\n```javascript\n// TODO 1-5\nasync function sendDeleteMessage(ID) {\n    return new Promise(async resolve =\u003e {\n        await db.collection(\"messages\").doc(ID).delete()\n            .then(function () {\n                console.log('delete successful');\n                resolve({ 'success': true });\n            })\n            .catch(function (error) {\n                console.log(error);\n                resolve({ 'success': false });\n            });\n    });\n}\n```\n\n```\nGo to /home/kali/Documents/Flask-Firebase-Chat/frontend/index.html\nUse Ctrl+F5 for hard reload if changes not appearing\n```\n\nYou should now be able to get, add, and delete messages.\n\n\u003cimg src=\"assets/step9_sol.gif\"\u003e\n\n## Step 10: Editing a message\n\nTo begin working on this step, switch to the ``Step10-StarterCode`` branch.\n\n```bash\ngit checkout Step10-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step10-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_10.png\"\u003e\n\n___\n\n\nTo edit a message, we can call ``onUpdate()`` on the reference to the document to edit. We can then pass the parameters to update in a JSON object passed to ``onUpdate()``. In our case, message is the only parameter of a document that we want to edit.\n\nAdditionally, we use a ``.then(function() {})`` and ``.catch(function(error) {})`` to execute code on success and on failure respectively.\n\n``custom_ajax_firebase.js``\n```javascript\n// TODO 1-6\nasync function sendPatchMessage(ID, message) {\n    return new Promise(async resolve =\u003e {\n        await db.collection(\"messages\").doc(ID).update({\n            message: message\n        })\n            .then(function () {\n                console.log('update successful');\n                resolve({ 'success': true });\n            })\n            .catch(function (error) {\n                console.log(error);\n                resolve({ 'success': false });\n            });\n    });\n}\n```\n\n```\nGo to /home/kali/Documents/Flask-Firebase-Chat/frontend/index.html\nUse Ctrl+F5 for hard reload if changes not appearing\n```\n\nYou should now be able to get, add, delete, and edit messages.\n\n\u003cimg src=\"assets/step10_sol.gif\"\u003e\n\n# 4. Firebase Hosting\n\n## Step 11: Hosting the Firebase version of the website with Firebase Hosting\n\nTo begin working on this step, switch to the ``Step11-StarterCode`` branch.\n\n```bash\ngit checkout Step11-StarterCode\n```\n\nIf you get an error, you might have to save the changes before running the above checkout command. Hence, save your changes using the below command.\n\n```bash\ngit add .\ngit commit -m \"xyz\"\n# Here xyz is any message describing the work you've done in the commit\n```\n\nBefore continuing, confirm you are in the ``Step11-StarterCode`` branch by looking at the bottom left of VS Code.\n\n\u003cimg src=\"assets/vscode_git_branch_11.png\"\u003e\n\n___\n\n\nIn order to host the Flask version of our website, we would need to host it on a VM or something similar. Hence, we would need to manage scalability, maintainance, and more.\n\nHowever, with Firebase Hosting, we can simply host the website on a serverless model. Firebase Hosting then automatially handles the scaling and maintainance behind the scenes.\n\nLet us host our Firebase version of our app with Firebase Hosting.\n\n1. Open a terminal in the project folder\n2. Install the Firebase command line tools (skip if using pre-setup VM or if already installed).\n\n```bash\nsudo npm install -g firebase-tools\n```\n\n3. Login to Firebase\n\n```bash\nfirebase login\n```\n\n4. Initialize the project directory as a Firebase project\n\n```bash\nfirebase init\n```\n\n5. From the list of which features we want to set up, choose hosting using ``Space`` and confirm your selection with ``Enter``\n\n\u003cimg src=\"assets/firebase_hosting.png\"\u003e\n\n6. If you have already created a Firebase Project ( [Firebase Setup, Option #2 above](###option-2:-setup-a-new-firebase-project) ), choose ``Use an existing project`` and choose the created project. If you haven't created a new Firebase project, you can create one by following [these instructions](###option-2:-setup-a-new-firebase-project).\n\n\u003cimg src=\"assets/firebase_hosting_2.png\"\u003e\n\n7. For the public directory, choose ``frontend`` as all our frontend resources that we want to host is in the ``/frontend`` folder.\n\n8. As our website just has one webpage, configure it as a single page app. This redirects all other paths to the home page. ie. ``/`` and ``/abc`` route to the same HTML page: ``/frontend/index.html``.\n\n9. For our example, we will choose not to set up automatic builds with GitHub. However, you can select this option for your future projects to speed up the time between commiting your changes and seeing them appear on the hosted platform.\n\n10. Since we have already created our ``index.html`` file, we will not need the default ``index.html`` file. Hence, we don't need to overwrite our existing ``index.html`` file with the default one.\n\n\u003cimg src=\"assets/firebase_hosting_3.png\"\u003e\n\u003cimg src=\"assets/firebase_hosting_4.png\"\u003e\n\n11. Now, to host the website, simply use use the below command.\n\n```bash\nfirebase deploy\n```\n\n\u003cimg src=\"assets/firebase_hosting_5.png\"\u003e\n\nNow you can use the public url instead of the path to the local ``index.html`` file to access the website.\n\nYour public URL will be something similar to:\n\n```\nflaskchat-6684a.web.app\n```\n\nYou should now be able to access the Firebase version of the website on a public domain.\n\n\u003cimg src=\"assets/step11_sol.gif\"\u003e\n\n# Conclusion\n\nThat's all we have in our exercise. Congratulations on making it till the end! By following this exercise, you have some basic understanding of:\n\n* Flask servers\n* Integrating Flask with MySQL\n* Firebase Firestore backend\n* Firebase Hosting\n\nI hope you enjoyed this exercise and benefitted from it.\n\nIf you enjoyed this exercise repo, be sure to star it and [follow me](http://www.rohankadkol.com).\n\nThanks!\n\nGeaux Tigers! 🐯","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr100-stack%2Fflask-firebase-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fr100-stack%2Fflask-firebase-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr100-stack%2Fflask-firebase-chat/lists"}