Deploy on AWS using React, Flask, and SQLite.
Tags: sqlite, react, flask, aws
Published: 2023-07-30 20:22:35

Deploying to AWS can be difficult to learn. I hope to make this easy. 

Credits: Professor Andrew Awdeorio

## What you will get out of this 

You will have a very simple web app live on the internet. Click [here](http://ec2-3-136-156-17.us-east-2.compute.amazonaws.com/) and [here](http://ec2-3-136-156-17.us-east-2.compute.amazonaws.com/root) to see the end result. 

## Possible limitations

To ensure you don't waste your time. SQLite might not be the best option if the application that you hope to make with this is very data intensive. 

While this article is set up for image upload functionality to be possible. This is not covered in this tutorial. You can store references to images very easily with the functionality I use in this tutorial. Checking out my tutorial  on [image uploads with firebase](https://dev.to/itnext/how-to-do-image-upload-with-firebase-in-react-cpj) is a great option until I release a tutorial about that for this stack. 

## __Important:__ 

This tutorial does not go over best practices for securing your AWS account upon account creation. If you plan on building applications that hold sensitive user data hosted through AWS and not just this tutorial, I recommend learning about how to [secure your AWS account](https://aws.amazon.com/iam/resources/best-practices/) before you start building them. 


## Table Of Contents.
[Part 1: creating the database](#part-1)
[Part 2: creating a flask app](#part-2)
[part 3: Creating the react app](#part-3)
[part 4: Setting up an AWS account](#part-4)
[part 5: Deploying to AWS](#part-5)


## Introduction

In this tutorial we are going to walk through how to make a deployment to AWS with a simple SQLite server. How to make a flask app capable of interacting with that server to store and retrieve data. And how to make a react app hosted through flask. 

The application itself will be simple but you will able host powerful web applications after completing this tutorial.

## Why this is useful

Completing this tutorial is a great way introduction to hosting on aws. Even if this isn't your typical stack this will show the concepts to make your next deploy easier. 

It's a perfect introduction to scripting. I use commandline scripting in almost every type of project that I do becuase it helps me save so much time in my workflows. 

This can also be used to integrate python apps into something that can be shared on the web. This is important for anyone who has a great python app but doesn't have a means of share that with the world.


## What you will need. 

A text editor. I will be using VS code. 
a command line interface. I will be using macOS with unix and zsh. I believe that the type of command line wont matter as long as you understand the corrisponding command for your operating system or look them up as needed.

[python3 installed](https://www.freecodecamp.org/news/python-version-on-mac-update/) on your host machine 3.10 or greater.

A [github account](https://github.com/) we will be using a repo to move all of our code base to an [ubuntu](https://aws.amazon.com/marketplace/pp/prodview-iftkyuwv2sjxi) instance on AWS since this is currently on the free tier. 


## prerequisite skills.

1. Basic understanding of python.
2. basic knowledge of flask.
3. Basic understanding of react. 
4. good understanding of unix command line.
   * we will use shell scripting in order to test things. if you understand bash this should be straightforward. We will also use many linux commands and I believe I have done a great job of making this as beginner friendly as possible. 
5. basic understanding of git. 
6. basic understanding of emacs is a plus but not necessary.

## Disclaimer 

While I have done many hours of due diligence on this article. Mistakes may still be in here. If you notice anything that isn't right, I am very open to constructive criticism and feedback. I'm also very willing to help anyone who is having trouble to complete this tutorial. I want to see you succeed in deploying a web-app if you are willing to give me your time to learn this and I take not wasting your time very seriously.  

 <a name="part-1"></a>

## Part 1: Making the database.

We are going to start by making a simple project directory. 

Go to the directory where you want this project in the terminal and paste this code snippet. 

```bash
mkdir webapp && cd webapp
```

next we have to install SQLite via [homebrew](https://brew.sh/) if on macOS.  


```bash
brew install sqlite3
```

you can make sure you have the right version by running this line in the terminal. 

```bash
sqlite3 --version
```
*output: 
-- Loading resources from /Users/tallan/.sqliterc
3.32.3 2020-06-18 14:16:19 02c34...<trailing chars>...*



Next we are making a very simple database with one table so that we can test if the database works properly.

To do this, we want to make a folder to keep 2 files. A small amount of dummy data and a schema. 

from the terminal inside the project directory, we make a new folder /sql

```bash
mkdir sql && cd sql
```

From the terminal, now inside the /sql directory, we make 2 files to host each of these. 


```bash
touch data.sql && touch schema.sql
``` 

now we make a folder for stock images. 

I wont provide the images but I took screen shots from an AI photo generator and added them to the following folder and named them after the [fake people(https://this-person-does-not-exist.com/en) that I'm about to add to the database. 

```bash
mkdir uploads
```

when you run this command you should have all the same filenames. 

```bash
tree uploads
```
output:
```bash
uploads
├── alicewilliams.png
├── bobjohnson.jpeg
├── charliebrown.jpg
├── janesmith.jpg
└── johndoe.png
```

Let's add the simple table to the __schema.sql__ file. 

Even though we don't have any foreign keys, this will be good to remember in case we decide to do anything else with the database after the tutorial. 

```sql
PRAGMA foreign_keys=ON;

CREATE TABLE developer (
    fullname TEXT CHECK(length(fullname) <= 40),
    email TEXT CHECK(length(email) <= 40),
    picture TEXT CHECK(length(picture) <= 64),
    password TEXT CHECK(length(password) <= 256),
    created DATETIME DEFAULT CURRENT_TIMESTAMP
);
```

Add some simple entries to the __data.sql__ file

```sql
INSERT INTO developer(fullname, email, picture, password)
VALUES('John Doe', 'john_doe@gmail.com', 'johndoe.png', 'password');

INSERT INTO developer(fullname, email, picture, password)
VALUES('Jane Smith', 'jane_smith@yahoo.com', 'janesmith.jpg', 'password');

INSERT INTO developer(fullname, email, picture, password)
VALUES('Bob Johnson', 'bob_johnson@outlook.com', 'bobjohnson.jpeg', 'password');

INSERT INTO developer(fullname, email, picture, password)
VALUES('Alice Williams', 'alice_williams@hotmail.com', 'alicewilliams.png', 'password');

INSERT INTO developer(fullname, email, picture, password)
VALUES('Charlie Brown', 'charlie_brown@gmail.com', 'charliebrown.jpg', 'password');
```

We move on to the bash scripting portion so that we can make setting up, taking down and reseting the database easy. 

make sure you move your terminal to the project directory.
*from inside the sql folder*
```bash
tree .
```
*output:
```
├── data.sql
├── schema.sql
└── uploads
    ├── alicewilliams.png
    ├── bobjohnson.jpeg
    ├── charliebrown.jpg
    ├── janesmith.jpg
    └── johndoe.png
```

Move back to the projects root directory. 
```bash
cd ..
```

Make a new folder for your bash script and the bash script file while staying in this project directory. 

```bash
mkdir bin && touch bin/db
```

I'm going to add these in parts with an explanation of code and will post the [whole file](#final-db) at the end. 

at the very top of the __db__ file paste this line.

```script
#!/bin/bash
```
this lets the computer know that this is a bash script, so when you run the file, it will be like running many terminal commands as once. 

below that line we will add commands to [echo the outputs](https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/) so that it's easy to spot errors in these commands. 

```bash
set -Eeuo pipefail
set -x
```

let's add a friendly usage message for when we don't add an argument. 

```bash
usage() {
  echo "Usage: $0 (create|destroy|reset|dump)"
}

if [ $# -ne 1 ]; then
  usage
  exit 1
fi
```

The first block is a function making it so that if we run `./bin/db` in the terminal, then there is text that is output to the terminal. 

The second block is saying if the number of terminal arguments used to call this script are not equal to 1 then call the usage function and exit 1. notice that the call to the script from the terminal (`./bin/db`) is not considered an argument. 

We have to run this line to enable permissions for __db__ to access the command line. 

```bash
chmod +x bin/db
```

Now you can run this script with this command.

```bash
./bin/db
```

if you don't run `chmod +x bin/db` before you call this script, you will get this error. 

output:
*bash: ./bin/db: Permission denied*


If you run it after entering `chmod +x bin/db` into the terminal then you will get the following output. 

*output:*
*+ '[' 0 -ne 1 ']'*
*+ usage*
*+ echo 'Usage: ./bin/db (create|destroy|reset|dump)'*
*Usage: ./bin/db (create|destroy|reset|dump)*
*+ exit 1*

Now we get to the commands themselves. 

To create a database, we make an else if statement that will run a block of commands based on the argument given when we call the script. 

This command will be `./bin/db create` and the code that will run when we call this will be. 

```bash
if [ "$1" = "create" ]; then
    mkdir -p var/uploads
    sqlite3 var/App.sqlite3 < sql/schema.sql
    sqlite3 var/App.sqlite3 < sql/data.sql
    cp sql/uploads/* var/uploads/
fi
```


Then in the terminal run. 

```bash
./bin/db create
```

if everything was coded correctly, and you ran `chmod +x bin/db` and then `./bin/db create` you should have a new folder var and a file called __App.sqlite3__ inside that folder. 

To check, run this command in the terminal

```bash
tree var
``` 

output:
```
var
├── App.sqlite3
└── uploads
    ├── alicewilliams.png
    ├── bobjohnson.jpeg
    ├── charliebrown.jpg
    ├── janesmith.jpg
    └── johndoe.png
```

We want to make a command for each functionality stated by the usage message. 

To do that we will add `elif`'s before the closing `fi` and after the command for create. 

the rest of the `elif` statement will look like this. 

```script
elif [ "$1" = "destroy" ]; then
    rm -rf var/App.sqlite3 var/
elif [ "$1" = "reset" ]; then
    ./bin/db destroy
    ./bin/db create
elif [ "$1" = "dump" ]; then
    sqlite3 var/App.sqlite3 .dump
``` 

<a name="final-db"></a>

The full __db__ file will look like this. 

```c
#!/bin/bash

set -Eeuo pipefail
set -x

usage() {
  echo "Usage: $0 (create|destroy|reset|dump)"
}

if [ $# -ne 1 ]; then
  usage
  exit 1
fi

if [ "$1" = "create" ]; then
    mkdir -p var/uploads
    sqlite3 var/App.sqlite3 < sql/schema.sql
    sqlite3 var/App.sqlite3 < sql/data.sql
    cp sql/uploads/* var/uploads/
elif [ "$1" = "destroy" ]; then
    rm -rf var
elif [ "$1" = "reset" ]; then
    ./bin/db destroy
    ./bin/db create
elif [ "$1" = "dump" ]; then
    sqlite3 var/App.sqlite3 .dump
fi
```

Make sure that when you run the following terminal commands that you get the same outputs before moving onto the flask portion. You will be using these commands often.

*From the project directory with no database created*
```bash
./bin/db create
```
output:
```
+ '[' 1 -ne 1 ']'
+ '[' create = create ']'
+ mkdir -p var
+ sqlite3 var/App.sqlite3
+ sqlite3 var/App.sqlite3
```

```bash
./bin/db destroy
```

output:
```bashe
+ '[' 1 -ne 1 ']'
+ '[' destroy = create ']'
+ '[' destroy = destroy ']'
+ rm -rf var
```


```bash
./bin/db reset
```
output:
```bash
+ '[' 1 -ne 1 ']'
+ '[' reset = create ']'
+ '[' reset = destroy ']'
+ '[' reset = reset ']'
+ ./bin/db destroy
+ '[' 1 -ne 1 ']'
+ '[' destroy = create ']'
+ '[' destroy = destroy ']'
+ rm -rf var
+ ./bin/db create
+ '[' 1 -ne 1 ']'
+ '[' create = create ']'
+ mkdir -p var/uploads
+ sqlite3 var/App.sqlite3
+ sqlite3 var/App.sqlite3
+ cp sql/uploads/alicewilliams.png sql/uploads/bobjohnson.jpeg sql/uploads/charliebrown.jpg sql/uploads/janesmith.jpg sql/uploads/johndoe.png var/uploads/
```

```bash
./bin/db dump
```
output:
```bash
+ '[' 1 -ne 1 ']'
+ '[' dump = create ']'
+ '[' dump = destroy ']'
+ '[' dump = reset ']'
+ '[' dump = dump ']'
+ sqlite3 var/App.sqlite3 .dump
-- Loading resources from /Users/tallan/.sqliterc
PRAGMA foreign_keys=ON;
BEGIN TRANSACTION;
CREATE TABLE developer (
    fullname TEXT CHECK(length(fullname) <= 40),
    email TEXT CHECK(length(email) <= 40),
    picture TEXT CHECK(length(picture) <= 64),
    password TEXT CHECK(length(password) <= 256),
    created DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO developer VALUES('John Doe','john_doe@gmail.com','johndoe.png','password','2023-07-23 22:43:59');
INSERT INTO developer VALUES('Jane Smith','jane_smith@yahoo.com','janesmith.jpg','password','2023-07-23 22:43:59');
INSERT INTO developer VALUES('Bob Johnson','bob_johnson@outlook.com','bobjohnson.jpeg','password','2023-07-23 22:43:59');
INSERT INTO developer VALUES('Alice Williams','alice_williams@hotmail.com','alicewilliams.png','password','2023-07-23 22:43:59');
INSERT INTO developer VALUES('Charlie Brown','charlie_brown@gmail.com','charliebrown.jpg','password','2023-07-23 22:43:59');
COMMIT;
```

 <a name="part-2"></a>

## Part 2: Making the flask App. 

*Ensure part 1 works properly before continuing to part 2.* 

We are going to want to make a custom python environment that is specific to this directory

This will mean we need to define a requirements.txt that's used for defining all the packages.

If you mess up at any point in trying to do this section run this command in the project directory and it will remove the python env folder that we are about to create. 

```bash
rm -rf env
```

To ensure that we have python3 on your machine, run this command. 

```bash
python3 --version
```

output:
*Python 3.11.4*

You may have a different version but, as long as this is 3.10 or greater you don't have to update to succeed in this tutorial.

This also assumes that you are not using a python version from Anaconda. 

Another gotcha is that when we run this command it should be a blank output.

```bash
printenv PYTHONPATH
```
if you do have an output when you run this, you can run this command, but you will have to do so everytime you restart the terminal. 

```bash
env --unset PYTHONPATH
```

If all the above checks passed, we can run this command.

```bash
 python3 -m venv env
```

This will have created a python environment local to this directory and this command will need to be ran every time we restart the terminal regardless of whether you had an ouput on the command before the one above. If we don't then the flask App will fail to start.

To activate the environment we run this command. 

```bash
source env/bin/activate
```

There should be something that says (env) to the left most side of our textbar where we write to the terminal.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8bp2ir209jxjsfqgsqy.png)  

To double check, run this command and it should output the env directory first.

```bash
 which -a python
```


output:
```bash
/Users/tallan/Desktop/article-writing/tutorials/flask-app/project/env/bin/python
/usr/local/bin/python
/usr/bin/python
``` 

Now we want to install [jinja2](https://jinja.palletsprojects.com/en/3.1.x/) since this is what will allow us to mount the DOM to the flask App with react. It can also be very useful for making simple webpages like forms that are used in addition to react since they can be more responsive and easier to make than full react components, when the page is a simple form or needs little to no database calls, I prefer to use it. 

```bash
./env/bin/pip install jinja2
```
 

We want to add additional packages to our starter files. To do this we will use a requirements.txt file. 

from the terminal make a requirements.txt file

```bash
touch requirements.txt
```

copy and paste the following code snippet into the file.
___requirements.txt__

```json
arrow==1.2.3
astroid==2.15.0
attrs==22.2.0
beautifulsoup4==4.11.1
bs4==0.0.1
certifi==2022.12.7
charset-normalizer==3.0.1
click==8.1.3
dill==0.3.6
exceptiongroup==1.1.0
Flask==2.2.2
html5validator==0.4.2
idna==3.4
iniconfig==2.0.0
isort==5.12.0
itsdangerous==2.1.2
Jinja2==3.1.2
lazy-object-proxy==1.9.0
MarkupSafe==2.1.2
mccabe==0.7.0
packaging==23.0
platformdirs==2.6.2
pluggy==1.0.0
pycodestyle==2.10.0
pydocstyle==6.3.0
pylint==2.16.2
pytest==7.2.1
python-dateutil==2.8.2
PyYAML==6.0
requests==2.28.2
six==1.16.0
snowballstemmer==2.2.0
soupsieve==2.3.2.post1
tomli==2.0.1
tomlkit==0.11.6
typing_extensions==4.4.0
urllib3==1.26.14
Werkzeug==2.2.2
wrapt==1.14.1
```

When you run the following command, this will install all the packages in the requirements.txt file to the virtual environment.

with our env activated and the txt file with all the pages above copied. Let's run the following command. 

```bash
pip install -r requirements.txt
```

We may get some depreciation warnings, that shouldn't be an issue. 

Run the env activate command again. 

```bash
source env/bin/activate
```

We are ready to make the flask App itself. 

Lowercase app is a reserved word in flask and I didn't want to call this package tutorial and so I settled for uppercase App. be mindful that there is a distinction between the 2. 

We are going to house most of the App in a folder of the same name.

```bash
mkdir App
```

we need an \_\_init\_\_.py file to initialize the package. 

```bash
touch App/__init__.py
```

\_\_init\_\_.py
```python
"""App package initializer."""
import flask

# app is a single object used by all the code modules in this package
app = flask.Flask(__name__)  # pylint: disable=invalid-name

# Read settings from config module (insta485/config.py)
app.config.from_object('App.config')

# Overlay settings read from a Python file whose path is set in the environment
# variable INSTA485_SETTINGS. Setting this environment variable is optional.
# Docs: http://flask.pocoo.org/docs/latest/config/
#
# EXAMPLE:
# $ export APP_SETTINGS=secret_key_config.py
app.config.from_envvar('APP_SETTINGS', silent=True)

# Tell our App about views and model.  This is dangerously close to a
# circular import, but Flask was designed that way.
# (Reference http://flask.pocoo.org/docs/patterns/packages/)
import App.views
import App.model  
```

Next we make a __config.py__ file for environment variables specific to the flask app. 

Inside the App directory make a file called config.py

```bash
touch App/config.py
```

_config.py_
```
"""App development configuration."""

import pathlib

# Root of this application, useful if it doesn't occupy an entire domain
APPLICATION_ROOT = '/'

# Secret key for encrypting cookies
SECRET_KEY = b'FIXME SET WITH: $ python3 -c "import os; print(os.urandom(24))"'
SESSION_COOKIE_NAME = 'login'

# File Upload to var/uploads/
APP_ROOT = pathlib.Path(__file__).resolve().parent.parent
UPLOAD_FOLDER = APP_ROOT/'var'/'uploads'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
MAX_CONTENT_LENGTH = 16 * 1024 * 1024

# Database file is var/App.sqlite3
DATABASE_FILENAME = APP_ROOT/'var'/'App.sqlite3'
```

As the TODO suggests, run the following command and replace the `SECRET_KEY` assignment with the following command.

```bash
python3 -c "import os; print(os.urandom(24))"
```
example output:
```bash
b'\x9a\xbb4\x86\x1d7\xac\x1ad\x14\xd9:\xcc\xf3\xf4\r\xf3\xd7\xd3cd\xfc$\xae'
```
Now we have to make a static directory to hold css and placeholder images, I wont be providing any code for this but this is folder is where you can house it for your project. If you don't use any style within your react application or the jinja templates. This is the command.

```bash
mkdir App/static
```

Now lets make a folder for templates inside the App directory to act as a place to put html and jinja template files. 

We will also make an index.html in the same command. 

```bash
mkdir App/templates && touch App/templates/index.html
```

We'll add simple html to use for testing if our flask app is working.

```html
<!DOCTYPE html>
<html lang="en">
Hello world!
</html>
``` 

Now we need to make a views model. This is where you will keep python functions that are called when a user visits a url. 

```bash
mkdir App/views 
```

Next we need to make an \_\_init\_\_.py to access our views functions app wide and an index.py file to hold our function specific to the `'/'` route.

```bash
touch App/views/index.py && touch App/views/__init__.py
```

Inside this index.py we will make our first flask function. 

```python
"""
index (main) view.

URLs include:
/
"""
import flask
import App


@App.app.route('/')
def show_index():
    """Display / route."""
    context = {}
    return flask.render_template("index.html", **context)
```

Inside the views/\_\_init\_\_.py we add the import. 

```python
"""Views, one for each App's page."""
from App.views.index import show_index
```

We will add a placeholder file model.py. Even though we wont add any code to it for the moment, eventually this file will house the code for the database connection. 

```bash
 touch App/model.py
```

We still don't have a way for flask to understand that we have an App package to use. To fix this we will create a .toml file. 

in the project directory add this file. 

```bash
touch pyproject.toml
```

Add the following text to the *pyproject.toml*. 


```toml
[build-system]
requires = ["setuptools>=64.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "App"
version = "0.1.0"
dependencies = [
    "arrow",
    "bs4",
    "Flask",
    "requests",
]
requires-python = ">=3.10"

[tool.setuptools]
packages = ["App"]
```
Double check that you are in your python virtual environment,
now we have to install App into the virtual environment. 

```bash
source env/bin/activate
```

and then 

```bash
pip install -r requirements.txt && pip install -e .
```

You should receive the following output. 

output:
*a bunch of build info..*
*...*
*Successfully built App*
*Installing collected packages: App*
*Successfully installed App-0.1.0*


if everything was done correctly you should be able to see hello world by running the following command. 

```bash
flask --app App --debug run --host 0.0.0.0 --port 4000
```

and going to [localhost:4000](http://localhost:4000/)

Exit the terminal for the flask app by pressing `control + c` when you are finished checking that you see hello world at the local host. 

In preparation to move onto the part where we connect the database to the App, we are going to make a run script so we don't have to type a long command in everytime we want to start the server. 

```bash
touch ./bin/run
```

And add the following code to the script.

```bash
#!/bin/bash
set -Eeuo pipefail
set -x

export FLASK_ENV=development
export FLASK_APP=App
export FLASK_DEBUG=1

usage() {
  echo "Usage: $0"
}

if [ $# -ne 0 ]; then
  usage
  exit 1
fi



# If var/App.sqlite3 does not exist, print an error and exit non-zero.
    if [ ! -f var/App.sqlite3 ]; then
        echo "Error: var/App.sqlite3 does not exist. Run ./bin/db create."
        exit 1
    fi


flask --app App --debug run --host 0.0.0.0 --port 4000
```

Enable the script. 

```bash
chmod +x bin/run
```

Run the script to ensure no errors were introduced. 

```bash
./bin/run
```

You should now be able to visit [localhost:4000](http://localhost:4000/) the same as before. 

let's start by resetting the database. 

```bash
./bin/db reset
```

Ensure that you can interact with the database from the terminal first. 

```bash
sqlite3 var/App.sqlite3 "SELECT fullname, email FROM developer;"
```

output:
*-- Loading resources from /Users/tallan/.sqliterc*
*fullname    email*     
*----------  ------------------*
*John Doe    john_doe@gmail.com*
*Jane Smith  jane_smith@yahoo.c*
*Bob Johnso  bob_johnson@outloo*
*Alice Will  alice_williams@hot*
*Charlie Br  charlie_brown@gmai*

Copy and paste the following code into the blank model.py file. 

```python
"""App model (database) API."""
import sqlite3
import flask
import App


def dict_factory(cursor, row):
    """Convert database row objects to a dictionary keyed on column name.

    This is useful for building dictionaries which are then used to render a
    template.  Note that this would be inefficient for large queries.
    """
    return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}


def get_db():
    """Open a new database connection."""

    if 'sqlite_db' not in flask.g:
        db_filename = App.app.config['DATABASE_FILENAME']
        flask.g.sqlite_db = sqlite3.connect(str(db_filename))
        flask.g.sqlite_db.row_factory = dict_factory

        # Foreign keys have to be enabled per-connection.
    
        flask.g.sqlite_db.execute("PRAGMA foreign_keys = ON")

    return flask.g.sqlite_db


@App.app.teardown_appcontext
def close_db(error):
    """Close the database at the end of a request."""

    sqlite_db = flask.g.pop('sqlite_db', None)
    if sqlite_db is not None:
        sqlite_db.commit()
        sqlite_db.close()
```

Before we can display anything to the browser, we have to account for how to handle images. 

Let's make a file in the views folder called images.py

```bash
touch App/views/images.py
```

Add the following code to this file. 

```python
import flask
from flask import  send_from_directory
import App


@App.app.route('/uploads/<path:filename>', methods=['GET'])
def download_file(filename):
    """Download a file."""
    return send_from_directory(App.app.config['UPLOAD_FOLDER'],
                               filename, as_attachment=True)
```

Now add this function to the view/\_\_init\_\_.py so that we can find files in the uploads folder. 

*view/\_\_init\_\_.py*
```python
"""Views, one for each app's page."""
from App.views.index import show_index
from App.views.images import download_file
```



In this file we have a way for flask to get reference images based on the string names stored in the database. 



Now __change__ the *views/index.py* to have a database call for all users except one. 

```python
"""
index (main) view.

URLs include:
/
"""
import flask
import App


@App.app.route('/')
def show_index():
    """Display / route."""
        # Connect to database
    connection = App.model.get_db()

    # Query database
    logname = "John Doe"
    cur = connection.execute(
        "SELECT  fullname, email, picture "
        "FROM developer "
        "WHERE fullname != ?",
        (logname, )
    )
    devs = cur.fetchall()

    
    context = {"devs": devs}
    return flask.render_template("index.html", **context)
```

And change the index.html to display that information with jinja. 


```python
<!DOCTYPE html>
<html lang="en">
    <h1>developer</h1>
    {% for dev in devs %}
    <p>{{dev.fullname}}, </p>
    <p>{{dev.email}} </p>
    <img src="{{ url_for('download_file', filename=dev.picture) }}" alt="image" width="100" height="100">
    <br />
    {% endfor %}
</html>
```

If our development server is on by running `./bin/run`, when we visit [localhost:4000](http://localhost:4000/) we should see the names of the developers from the server except John Doe.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pqpgfe5656vyk11wsz95.png)

This completes the flask portion of this tutorial. 

 <a name="part-3"></a>

## Part 3: Making the react app. 

We are going to make our react app in a slightly different way that normal so that it will make the instantiation on AWS easier. Doing it this was will make it so that we can run the front-end and back-end with a single process.

Let's jump in. 

Make sure that you have a version of [node](https://formulae.brew.sh/formula/node) greater than 19.8.1

```bash
node --version
```
and [npm](https://www.google.com/search?q=npm+install+homebrew&rlz=1C5CHFA_enUS905US906&sxsrf=AB5stBi8b3Uv0B32ZW6uY48kqYjKtMULSw%3A1690316889275&ei=WTDAZOWhEO-pptQPzs-YqAs&ved=0ahUKEwilsviN2aqAAxXvlIkEHc4nBrUQ4dUDCBA&uact=5&oq=npm+install+homebrew&gs_lp=Egxnd3Mtd2l6LXNlcnAiFG5wbSBpbnN0YWxsIGhvbWVicmV3MgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABhHGNYEGLADMgoQABiKBRiwAxhDMgoQABiKBRiwAxhDSMQDUABYAHABeAGQAQCYAQCgAQCqAQC4AQPIAQDiAwQYACBBiAYBkAYK&sclient=gws-wiz-serp) version greater than 9.5.1.

this should have installed npm as a package with your node install. 

```bash
npm --version
```

Now let's make an App/js folder to keep react components.

```bash
mkdir App/js
```

And a Index.js file to keep our dom root div. 

```bash
touch App/js/Index.js
```

And add the following code to this file. 

```javascript
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const root = createRoot(document.getElementById("root"));


root.render(<App />);
```

And the App component which will be the starting point of all the react code. 

```bash
touch App/js/App.js
```

add the following code to the App.js file. 

```javascript
import React from "react";

const App = () => {
  return (
    <div>
        Hello React!
    </div>
  );
};

export default App;
```

Next we need a package json to define all of the packages for webpack and react to use. 

```bash
touch package.json
```

Add this code to the package.json file. 

```json
{
    "name": "App",
    "version": "1.0.0",
    "description": "App front end",
    "main": "App/js/Index.jsx",
    "author": "awdeorio",
    "license": "MIT",
    "repository": {},
    "devDependencies": {
      "@babel/core": ">=7.21.3",
      "@babel/plugin-transform-runtime": ">=7.21.0",
      "@babel/preset-env": ">=7.20.2",
      "@babel/preset-react": ">=7.18.6",
      "@babel/runtime": ">=7.21.0",
      "@cypress/grep": "^3.1.5",
      "@types/react": ">=18.0.28",
      "@types/react-dom": ">=18.0.11",
      "babel-loader": "^9.1.2",
      "eslint": ">=8.36.0",
      "start-server-and-test": "^2.0.0",
      "tmp": "^0.2.1",
      "webpack": ">=5.76.2",
      "webpack-cli": ">=5.0.1"
    },
    "dependencies": {
      "latest-version": "^7.0.0",
      "moment": ">=2.29.4",
      "prop-types": ">=15.8.1",
      "react": "^18.2.0",
      "react-dom": "^18.2.0",
      "ts-loader": ">=9.4.2",
      "typescript": ">=5.0.2"
    },
    "engines": {
      "node": ">=18.0.0"
    }
  }
```

Next, we want to make a package-lock.json from the package.json. 

To do this run the following command. 

```bash
npm i --package-lock-only
```

Now run this command to make a node modules folder. 

```bash
npm ci .
```

Now we want to make sure that we have webpack with the right version. 

```bash
npx webpack --version
```

The output should look something like this but results may very slightly. 

output:
```
 System:
    OS: macOS 11.7.6
    CPU: (8) x64 Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
    Memory: 276.08 MB / 16.00 GB
  Binaries:
    Node: 20.3.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 9.6.7 - /usr/local/bin/npm
  Browsers:
    Chrome: 115.0.5790.102
    Safari: 16.4.1
  Packages:
    babel-loader: ^9.1.2 => 9.1.3 
    ts-loader: >=9.4.2 => 9.4.4 
    webpack: >=5.76.2 => 5.88.2 
    webpack-cli: >=5.0.1 => 5.1.4 
```

We want create a root.html and make a root div for the react-dom to mount onto. 

```bash
touch App/templates/root.html
```

*App/templates/root.html*
```html
<!DOCTYPE html>
<script type="text/javascript" src="{{ url_for('static', filename='js/bundle.js') }}"></script>
<html lang="en">
    <body>
        <!-- Plain old HTML and jinja2 nav bar goes here -->
      
      
        <div id="root">
          Loading ...
        </div>
        <!-- Load JavaScript -->
        <script type="text/javascript" src="{{ url_for('static', filename='js/bundle.js') }}"></script>
      </body>
</html>
```

This is telling the flask app to display whatever is in the bundle.js in the root div.

We want to make a webpack.config.js to keep our build configurations for react.

In the project directory run the following command. 

```bash
touch webpack.config.js
```

And add this code to the *webpack.config.js*.

```c
const path = require("path");
const { existsSync } = require("fs");

// Set the entrypoint to Index.jsx by default, but Index.tsx if using TypeScript.
let entry = "./App/js/Index.js";
if (existsSync("./App/js/Index.ts")) {
  entry = "./App/js/Index.ts";
}

module.exports = {
  mode: "development",
  entry,
  output: {
    path: path.join(__dirname, "/App/static/js/"),
    filename: "bundle.js",
  },
  devtool: "source-map",
  module: {
    rules: [
      {
        // Test for js or jsx files
        test: /\.jsx?$/,
        // Exclude external modules from loader tests
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env", "@babel/preset-react"],
          plugins: ["@babel/transform-runtime"],
        },
      },
      {
        // Support for TypeScript in optional .ts or .tsx files
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx"],
  },
};
```

Compile the react code in preparation for a run test.

```bash
npx webpack
```

In the same way that we made a index.py we want to make a root.py to display the react app. 

```touch
touch App/views/root.py
```

Add the following code. 

```python
"""
root of react app.

URLs include:
/root/
"""
import flask
import App


@App.app.route('/root/')
def show_root():
    """Display / root for react application."""
        # Connect to database

    context = {}
    return flask.render_template("root.html", **context)
```

Now add this function to the \_\_init\_\_.py __within the views__ folder. the whole file will look like this. 

*App/views/\_\_init\_\_.py*
```python
"""Views, one for each app's page."""
from App.views.index import show_index
from App.views.root import show_root
from App.views.images import download_file
```

You should not receive any errors but might get warnings. 

Use the script we made earlier to run the flask app.

```bash
./bin/run
```

You should not get an error visit [localhost:4000/root](http://localhost:4000/root)

Also ensure that our jinja index page still has our developer names. 

![site that says 'hello react!'](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xk9d6idqqux2geazyajg.png)

When you want to develop and make changes to the the react app and not type `npx webpack` everytime, you can use the `--watch` flag. this will work the same way as `npm start`

After we are finished checking the browser. Let's shut down the flask app by clicking on the terminal and pressing `control + c`.


```bash
npx webpack --watch
```

npm packages can be installed as normal with `npm i <package-name>`. 

The only major difference between how you interact with this react app vs. one mode with create-react-app is the front-end is essentially ran through the back-end.


We are now ready to develop a front-end with react and a back-end with flask.

We can also make jinja templates in this app when the situation fits. 

This completes the react portion of this tutorial. 

<a name="part-4"></a>

## Part 4: Setting up an AWS account. 

Its a good idea to check out the [best practices](https://aws.amazon.com/iam/resources/best-practices/) for setting up AWS accounts since this is easy to forget about account security after you have had an account for a while. 

Before we can deploy to AWS we first have to set up an aws account. If you already have an account set up, sign into your AWS account you can skip this section by clicking [here](#aws-setup)

Go to this url to [create an account](https://portal.aws.amazon.com/billing/signup#/start/email).


![AWS account signup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8x6kar6sjsp2dsj2gbxs.png)

They will send a verification code to your email. 

After entering the code you will enter the will have to make a password. 

Then you will be sent to this page. 

![AWS personal form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9bzzazxgd6v1cjpdn2dv.png)


You will need to add a credit card to your account even though the setup we are making is free.


![AWS billing page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3x3hl7ahg1em7papns8k.png)

Confirm your Identity. 


![AWS confirm Identity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ob1poryz3xf8a1ujg67.png)

After you confirm your phone number, choose which tier. We will use the free tier. Any tier you use will not effect the rest of this tutorial. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywdfbf42q7rydqcqzfjd.png)

This will redirect you to your AWS sign-in. 

Log into you account and you will be directed to this page. 


Click on the settings icon in the top left corner of the page. 

<a name="aws-setup"></a>

![AWS dashboard with arrows to the settings tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khqcgiglcor92scclw93.png)


![settings dash board with arrows to point to what to click on.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stih6j7gikgcya3ougsa.png)

Now click to launch a new instance. 

![arrow to launch instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kd8plb5pa315l2gxzw64.png)

We are going to use an ubuntu instance. Click on Ubuntu where you see all the different virtual boxes. 

Scroll down to where it says add a key pair(login) and name it.

If you start typing in the text box, a modal will popup where you can enter the name of the key. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m36521vpfh4dia87d5ps.png)

Whether you added a key pair in the model or added directly to the textbox does not matter. As long as we choose RSA and .pem this will be fine. 

Click add key pair once you named it. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5gh9rkocq4yqdq41o8fg.png)

This will download a aws-tutorial.pem file to you computer. 

Scroll down to network settings. 

Click on Allow HTTP traffic. __This part is important because you will be able to do everything else in this tutorial but wont be able to ever see the website even though you did everything else right!__ 

You can also allow HTTPS traffic but I have not tested this. you will need to buy an SSL certificate too. If someone gets this to work with allowing https traffic please let us know in the comments and how to do it. 

Now click launch instance. 


![arrow to launch instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0p4ce8t1c64y8frg5yji.png)

Scroll to the bottom and click view all instances. 


![arrow to view all instances](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1yfcpssj0nv8rfvyaou0.png)

You will be forwarded to a page with information about all of your instances. When you click the checkbox for the instance you just created, information about that instance will be displayed on the bottom of the page. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77e449hgqo3fg3dob1hw.png)

When you go the the address it will try and load for a while but no website will pop up. 

We will add the code from the previous parts of this section to be the website in the next section. 

This completes the AWS console setup portion of this tutorial. 

<a name="part-5"></a>

## Part 5: Deploying to AWS. 

This is the most complex portion the tutorial. __Be sure to read carefully__ because a small error in this section can cause hours of headache if you don't do this step by step. But at the end of this you will have a website ready for development, live on the internet and the ability to deploy other websites the same way. 

If you stuggled with earlier parts of this tutorial, you can copy [my repo](https://github.com/TallanGroberg/aws-deploy-tutorial) to make sure that none of the errors are from any previous sections which can make debugging easier. 

I recommend trying to do this on your own first.

First thing is to make a github repo. 

for extra data safety make a .gitignore file and add the .pem file to it. 

```bash
touch .gitignore
```

we want to add a lot folders to the gitignore since most of these are boilerplate files and not files that we have created. 

```txt
*.pem
node_modules
env
App.egg-info
__pycache__
``` 

This will prevent any private keys from being published to github. 

make an initial repo

```bash
git init && git add . && git commit -m "initialize"
```

create a [remote origin](https://www.google.com/search?q=remote+repository&rlz=1C5CHFA_enUS905US906&oq=remote+repository+&aqs=chrome..69i57j0i512l9.8710j0j7&sourceid=chrome&ie=UTF-8) and push it to github, which you will have to do on your own, leave it public for now, at least until you pull it into your ubuntu instance which we will do shortly. Then you can make it private if you wish. 


If you completed the previous sections, you should have a aws-tutorial.pem file in your downloads folder. 

Ensure you are in your project directory of your local repo, then run the following command. 

```bash
mv ~/Downloads/aws-tutorial.pem .
```

we can also drag and drop into the project directory. As long as we have it in the root of this folder of your __local__ repo, we are fine. 

![code showing .pem file is in the project directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsq2te4x6i5nivx71smq.png)

Now we give this a special [400 permission](https://www.google.com/search?q=chmod+400+permissions&rlz=1C5CHFA_enUS905US906&oq=chmod+400+permissions&aqs=chrome..69i57j0i22i30l2j0i390i650l4.8527j0j7&sourceid=chrome&ie=UTF-8) to the aws-tutorial.pem file for read only access so that we don't modify our key to the aws instance. 

Run this command. 

```bash
chmod 400 aws-tutorial.pem
```



We are now going to tunnel into our aws instance which will take use to a ubuntu terminal on aws that we control from our host machine. 

To do this you want to run the following command. 

Do not include the <>'s in the command but do include the ubuntu@

```bash
ssh -i aws-tutorial.pem ubuntu@<your public ipv4 DNS from the AWS cosole>
```

we can copy and past from our AWS console like so. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6r7q0kq6qejy9mja1jgp.png)

```bash
ssh -i aws-tutorial.pem ubuntu@<paste what you just copied here>
```

Type yes and enter to finish connecting. 

You should have a proper ssh tunnel to your ubuntu instance. It should look like this.

If it didn't work. Make sure that you have your aws-tutorial.pem file in the project directory. That you ran `chmod 400 aws-tutorial.pem` and that the command `ssh -i aws-tutorial.pem ubuntu@<public ipV4 DNS>` is correct and without the angle brackets. Note that everytime you shut down and restart you instance you will have to recopy this public ipV4 DNS.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vwmf9s5jkg13a3uarevu.png)

Since we are now going to be working with a linux command line interface, the commands will be a bit different. 

Now we want to install [nginx](https://en.wikipedia.org/wiki/Nginx) which will give our ubuntu server a way host a website. 

```bash
sudo apt-get update
```

```bash
sudo apt-get install nginx
```

type Y to add nginx. 


![accepting nginx on commandline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3im2w3ir297uc25548o.png)

when you visit the site remember to make sure that it's `http::/` and not `https::/` when you do, you should see this page now. 


![nginx default page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8iilrkyi9bfpxd7ow4xf.png)

We are going to have to change some files to get a configuration for our app to work correctly with njinx. 

To accomplish this we will use [emacs](https://www.google.com/search?q=emacs+cheat+sheet+mac&rlz=1C5CHFA_enUS905US906&oq=emacs+cheat+sheet&aqs=chrome.1.69i57j0i512l2j0i22i30l7.9144j0j7&sourceid=chrome&ie=UTF-8) to alter files. 


```bash
sudo apt-get install emacs-nox
```

Accept this install like last time. 

Also run this command. 

```bash
export EDITOR=emacs
```

This command will give no output. 

Having emacs means we will be using nano. 

Check that we have it.

```bash
which nano
```
output:
/usr/bin/nano

Now we will create a nginx.conf file to define how nginx will interact with our web app. 

```bash
sudo nano /etc/nginx/nginx.conf
```

For reference, when I write `control + k` the plus is to indicate that you press control and k keys at the same time. 

Hold `control + k` in order to delete all the lines in the file line by line. 

after the file is completely blank, copy and paste this text using `control + v`

```javascript
# Run nginx worker processes using user www-data, which should have been created after installing Nginx.
user www-data;

# Start as many workers as there are CPU cores.
worker_processes auto;

# Configure connection processing.
events {
	# Maximum number of simultaneous connections per worker process.
	worker_connections 1000;
}

# Configure the HTTP server.
http {
	# Directly copy data between file descriptors instead of storing it in a buffer.
	sendfile on;

	# Send the response headers and the beginning of a file in a single packet.
	tcp_nopush on;

	# Map file extensions to MIME types.
	include /etc/nginx/mime.types;

	# Specify where logs should be written.
	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	# Compress HTML data before sending it in responses.
	gzip on;

	# Include site-specific config files.
	include /etc/nginx/conf.d/*.conf;
}
```

Then press `control + x` then `y` for saving the file and `enter`

This will bring you back to the ubuntu terminal with the green text. 

Now we want to add special configuration details specific to our App itself. 

This will be a similar process as we just did but, we will be creating this file. 

Type this command into the terminal. 

```bash
sudo nano /etc/nginx/conf.d/App.conf
```

Copy and paste this file. 

```javascript
# Configure a virtual server. We only need one, because this machine will only host a single website.
server {
	# Configure all requests to the path /uploads.
	location /uploads {
		# Send a subrequest to /accounts/auth/. If the response is 200, proceed; if it's 403, don't
		# serve content, and just return a 403 status.
		#auth_request /accounts/auth/;

		# Serve the requested file from /var/www/uploads/<filename>.
		root /var/www;
	}

	# Configure all requests to the specific path /accounts/auth/.
	location = /accounts/auth {
		# Forward the request to the Flask app running at localhost:8000.
		proxy_pass http://localhost:8000;

		# Don't include the body of the request if any in the proxied request.
		proxy_pass_request_body off;

		# Set some headers that Nginx wants us to use for authentication subrequests.
		proxy_set_header Content-Length "";
		proxy_set_header X-Original-URI $request_uri;
	}

	# Configure all other requests to the server besides the ones that match above.
	location / {
		# Forward the request to http://localhost:8000 and return its response to the client.
		proxy_pass http://localhost:8000;

		# Make sure the proxied request's Host header is set to what the client intended.
		proxy_set_header Host $host;

		# Add a header to the proxied request indicating whether the protocol is HTTP or HTTPS.
		proxy_set_header X-Forwarded-Proto $scheme;

		# Add a header to the proxied request specifying the IP address of the original client.
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	}
}
```

there are some configurations in this tutorial that are based on the idea that this app will be much more heavy duty than what is given in this tutorial. Try uncommenting lines of code when you implement auth.

Press `control + x` then `y` then `enter`.


Now we want to restart the nginx server. 

```bash
sudo systemctl restart nginx
```

Install our python environment. 

```bash
 sudo apt-get install python3 python3-venv sqlite3
```

Now clone your remote repository. Alternatively you can use [my repo](https://github.com/TallanGroberg/aws-deploy-tutorial.git)

```bash
git clone https://github.com/TallanGroberg/aws-deploy-tutorial.git
```

Create a virtual environment for your project instance. 

```bash
python3 --version
```
output:
3.10.6

This is fine if its different that our local machine but it has to still be greater that 3.10

cd into your folder. 

```bash
cd aws-tutorial
```

We have one more file to change. Since we changed the default location for uploads in production. we have to alter our App's config.py to reference var/www instead of var/uploads. 

```bash
nano App/config.py
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ge5hiya2ykmsv33c3c2o.png)

You can use your mouse to scroll to a single line. And should be able to erase and type like in more common text editors. 

We should change `UPLOADS_FOLDER` to be this now. 

```bash
UPLOADS_FOLDER = pathlib.Path('/var/www/uploads')
```

Then press `control + x` then `Y` and finally `enter`

Create the virtual environment. 

```bash
python3 -m venv env
```

And activate. 

```bash
source env/bin/activate
```

 (env) should be at the left side of the green text. 

Run each of these commands one by one. 

```bash
pip install --upgrade pip setuptools wheel
```
```bash
pip install -r requirements.txt
```
```bash
pip install -e .
```

This next command to install [gunicorn](https://en.wikipedia.org/wiki/Gunicorn) will enable us to run a local server like in development but in production.

```bash
pip install gunicorn
```

Now we initialize the database.

```bash
./bin/db create
```
Copy the uploads folder to the var/www folder where nginx can use it.

```bash
 sudo cp -r var/uploads /var/www
```

Give ubuntu permissions for this new directory

```bash
sudo chown ubuntu:www-data /var/www/uploads
```

Now we install react related packages starting with npm and node. 

```bash
sudo apt-get install nodejs npm
```
You will notice that we don't have the same node version. 

When you see pink screens durning package downloads just press enter to get through them. They didn't cause me any problems when I picked the default everytime. 

```bash
node --version
```
output:
v12.22.9

Now we want to install everything from our __package.json__

```bash
npm install .
```

just to be sure make sure that your package-lock.json is here. 

```bash
npm i --package-lock-only
```

When this download is happening, we can see that we get alot of versioning warings. This will have to be addressed by updating node version manager, you can check out the github here ([nvm](https://github.com/nvm-sh/nvm)).

Run and update for ubuntu. 

```bash
sudo apt update
```

Install the version manager from its github directly. 

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
```

Now we have to source it to make sure ubuntu can take advantage of it. 

```bash
source ~/.bashrc
```

This will cause us the have to reactivate our python environment this means that we have to reinstantiate the whole package. 

```bash
source env/bin/activate
```

```bash
pip install --upgrade pip setuptools wheel
```
```bash
pip install -r requirements.txt
```

```bash
pip install -e .
```

Now we just install the node version that we had our react app running on in development. 

```bash
nvm install v20.3.0
```

Make sure that the node version update. 

```bash
node -v
```
output:
*v20.3.0*

This should ensure that when we run webpack without any errors. 

```bash
npx webpack
```

You should receive a compiled successfully in the last line of the output. 

We want to install `javascript-obfuscator` to make our javascript harder to understand for hackers. 

```bash
npm install javascript-obfuscator
```

Now we run the next few commands to compile, obfuscate and replace our original build. 

```bash
npx webpack
```

```bash
npx javascript-obfuscator App/static/js/bundle.js --reserved-strings '\s*'
```

press `y` to proceed. 

```bash
mv App/static/js/bundle-obfuscated.js App/static/js/bundle.js
```

Now we are going to get the server running.

Before we try and run it make sure that no other gunicorn processes are running. 

```bash
pkill -f gunicorn
```

When you run the next command, there should be no output.

```bash
pgrep -af gunicorn
```

Start the server in the background with this command. 

```bash
gunicorn -b localhost:8000 -w 2 -D App:app
```


Now when you visit your aws site, you should be able to see the app running. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lc7doz52a0sq8330meus.png)

If it's not, we can trouble shoot with this command which will give the log level output of what is happening. 

first.
```bash
pkill -f gunicorn
```

then.
```bash
gunicorn -b localhost:8000 -w 2 App:app --log-level debug
```

If you get an error about packages missing pertaining to python redo the virtual environment installation. You can do the same for react and that should do the trick.

## Conclusion 

I have not tried to develop anything beyond a single page react application with react with this configuration. I encourage comments about any react-packages that will not work with this configuration or if anyone could make the same functionality with create-react-app.

There is a way to write a script so that this deploy is way commands in the terminal. A great exercise would be to make this script on your own. 

Let me know what I could add to this article in the comments.