Getting Started

Chapter 4


In this chapter, we'll set up the base project structure.


To speed up development, we'll start with a pre-built project that features Flask, React, and Docker.

The code for this comes from the Flask and React microservices that were built in the Test-Driven Development with Python, Flask, and Docker and Authentication with Flask, React, and Docker courses. Be sure to review the courses for more details on how they were implemented.

Start by cloning down the project:

$ git clone https://gitlab.com/testdriven/flask-react-auth.git flask-react-aws
$ cd flask-react-aws

Main Commands

Set the following environment variable to define the base URL for AJAX requests from the React app:

$ export REACT_APP_USERS_SERVICE_URL=http://localhost:5001

Build the images:

$ docker-compose build

Take a quick look at the project structure, while the images are building:

├── .gitignore
├── .gitlab-ci.yml
├── Dockerfile.deploy
├── README.md
├── docker-compose.yml
├── release.sh
└── services
    ├── client
    │   ├── .dockerignore
    │   ├── .eslintrc.json
    │   ├── .gitignore
    │   ├── Dockerfile
    │   ├── Dockerfile.ci
    │   ├── README.md
    │   ├── coverage
    │   ├── package-lock.json
    │   ├── package.json
    │   ├── public
    │   │   ├── favicon.ico
    │   │   ├── index.html
    │   │   └── manifest.json
    │   └── src
    │       ├── App.jsx
    │       ├── components
    │       │   ├── About.jsx
    │       │   ├── AddUser.jsx
    │       │   ├── LoginForm.jsx
    │       │   ├── Message.jsx
    │       │   ├── NavBar.css
    │       │   ├── NavBar.jsx
    │       │   ├── RegisterForm.jsx
    │       │   ├── UserStatus.jsx
    │       │   ├── UsersList.jsx
    │       │   ├── __tests__
    │       │   │   ├── About.test.jsx
    │       │   │   ├── AddUser.test.jsx
    │       │   │   ├── App.test.jsx
    │       │   │   ├── LoginForm.test.jsx
    │       │   │   ├── Message.test.jsx
    │       │   │   ├── NavBar.test.jsx
    │       │   │   ├── RegisterForm.test.jsx
    │       │   │   ├── UserStatus.test.jsx
    │       │   │   ├── UsersList.test.jsx
    │       │   │   └── __snapshots__
    │       │   │       ├── About.test.jsx.snap
    │       │   │       ├── AddUser.test.jsx.snap
    │       │   │       ├── App.test.jsx.snap
    │       │   │       ├── LoginForm.test.jsx.snap
    │       │   │       ├── Message.test.jsx.snap
    │       │   │       ├── NavBar.test.jsx.snap
    │       │   │       ├── RegisterForm.test.jsx.snap
    │       │   │       ├── UserStatus.test.jsx.snap
    │       │   │       └── UsersList.test.jsx.snap
    │       │   └── form.css
    │       ├── index.js
    │       └── setupTests.js
    ├── nginx
    │   └── default.conf
    └── users
        ├── .coveragerc
        ├── .dockerignore
        ├── Dockerfile
        ├── Dockerfile.prod
        ├── entrypoint.sh
        ├── manage.py
        ├── project
        │   ├── __init__.py
        │   ├── api
        │   │   ├── __init__.py
        │   │   ├── auth.py
        │   │   ├── ping.py
        │   │   └── users
        │   │       ├── __init__.py
        │   │       ├── admin.py
        │   │       ├── models.py
        │   │       └── views.py
        │   ├── config.py
        │   ├── db
        │   │   ├── Dockerfile
        │   │   └── create.sql
        │   └── tests
        │       ├── __init__.py
        │       ├── conftest.py
        │       ├── pytest.ini
        │       ├── test_admin.py
        │       ├── test_auth.py
        │       ├── test_config.py
        │       ├── test_ping.py
        │       ├── test_user_model.py
        │       ├── test_users.py
        │       └── utils.py
        ├── requirements.txt
        └── setup.cfg

Important things to note:

  1. The "services" directory holds the two main services -- the server-side Flask API and the client-side React app.
  2. The docker-compose.yml file holds the configuration for building the following images:
    1. users: the Flask app
    2. users-db: the Postgres database
    3. client: the React app

Run the containers once the build is complete:

$ docker-compose up -d

Navigate to http://localhost:5001/ping in your browser.

You should see:

{
  "message": "pong!",
  "status": "success"
}

Review the Flask view for this route in the services/users/project/api/ping.py file.

Create the database:

$ docker-compose exec users python manage.py recreate_db

Seed the database:

$ docker-compose exec users python manage.py seed_db

Navigate to http://localhost:5001/users.

You should see:

{
  "status": "success",
  "data": {
    "users": [
      {
        "id": 1,
        "username": "michael",
        "email": "[email protected]",
        "active": true
      },
      {
        "id": 2,
        "username": "michaelherman",
        "email": "[email protected]",
        "active": true
      }
    ]
  }
}

Review the Flask view for this route, along the routes associated with the users endpoints in the services/users/project/api/users/views.py file.

Navigate to http://localhost:3007.

react user status

Manually test registering a new user, logging in, and logging out.

Review, then run the Pytest tests with code coverage:

$ docker-compose exec users pytest "project/tests" -p no:warnings --cov="project"

You should see:

======================================== test session starts ========================================
platform linux -- Python 3.7.4, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: /usr/src/app/project/tests, inifile: pytest.ini
plugins: cov-2.7.1
collected 38 items

project/tests/test_admin.py ..                                                                [  5%]
project/tests/test_auth.py ..............                                                     [ 42%]
project/tests/test_config.py ...                                                              [ 50%]
project/tests/test_ping.py .                                                                  [ 52%]
project/tests/test_user_model.py ...                                                          [ 60%]
project/tests/test_users.py ...............                                                   [100%]

----------- coverage: platform linux, python 3.7.4-final-0 -----------
Name                            Stmts   Miss  Cover
---------------------------------------------------
project/__init__.py                28      1    96%
project/api/__init__.py             0      0   100%
project/api/auth.py                78      6    92%
project/api/ping.py                 8      0   100%
project/api/users/__init__.py       0      0   100%
project/api/users/admin.py          7      0   100%
project/api/users/models.py        38      2    95%
project/api/users/views.py         84      5    94%
project/config.py                  20      0   100%
---------------------------------------------------
TOTAL                             263     14    95%


===================================== 38 passed in 1.54 seconds =====================================

Lint the Python code with Flake8:

$ docker-compose exec users flake8 project

Run Black and isort with check options:

$ docker-compose exec users black project --check
$ docker-compose exec users /bin/sh -c "isort project/*/*.py --check-only"

Need to make changes based on Black and isort recommendations?

$ docker-compose exec users black project
$ docker-compose exec users /bin/sh -c "isort project/*/*.py"

Run the client-side tests with coverage:

$ docker-compose exec client react-scripts test --coverage

Lint the JavaScript:

$ docker-compose exec client npm run lint

Run Prettier:

$ docker-compose exec client npm run prettier:check
$ docker-compose exec client npm run prettier:write

Other Commands

To stop the containers:

$ docker-compose stop

To bring down the containers:

$ docker-compose down

Want to force a build?

$ docker-compose build --no-cache

Remove images:

$ docker rmi $(docker images -q)

Postgres

Want to access the database via psql?

$ docker-compose exec users-db psql -U postgres

Then, you can connect to the database and run SQL queries. For example:

# \c users_dev
# select * from users;

Project Structure

Make sure you have a solid grasp of the project structure along with the above Docker commands. Again, review the Test-Driven Development with Python, Flask, and Docker and Authentication with Flask, React, and Docker courses for more details.

Let's refactor the project structure before configuring AWS for deployment.

Remove the following files:

  • .gitlab-ci.yml
  • Dockerfile.deploy
  • release.sh
  • services/client/Dockerfile.ci
  • services/users/Dockerfile.prod

The structure should now look like:

├── .gitignore
├── README.md
└── services
    ├── client
    │   ├── .dockerignore
    │   ├── .eslintrc.json
    │   ├── .gitignore
    │   ├── Dockerfile
    │   ├── README.md
    │   ├── coverage
    │   ├── package-lock.json
    │   ├── package.json
    │   ├── public
    │   │   ├── favicon.ico
    │   │   ├── index.html
    │   │   └── manifest.json
    │   └── src
    │       ├── App.jsx
    │       ├── components
    │       │   ├── About.jsx
    │       │   ├── AddUser.jsx
    │       │   ├── LoginForm.jsx
    │       │   ├── Message.jsx
    │       │   ├── NavBar.css
    │       │   ├── NavBar.jsx
    │       │   ├── RegisterForm.jsx
    │       │   ├── UserStatus.jsx
    │       │   ├── UsersList.jsx
    │       │   ├── __tests__
    │       │   │   ├── About.test.jsx
    │       │   │   ├── AddUser.test.jsx
    │       │   │   ├── App.test.jsx
    │       │   │   ├── LoginForm.test.jsx
    │       │   │   ├── Message.test.jsx
    │       │   │   ├── NavBar.test.jsx
    │       │   │   ├── RegisterForm.test.jsx
    │       │   │   ├── UserStatus.test.jsx
    │       │   │   ├── UsersList.test.jsx
    │       │   │   └── __snapshots__
    │       │   │       ├── About.test.jsx.snap
    │       │   │       ├── AddUser.test.jsx.snap
    │       │   │       ├── App.test.jsx.snap
    │       │   │       ├── LoginForm.test.jsx.snap
    │       │   │       ├── Message.test.jsx.snap
    │       │   │       ├── NavBar.test.jsx.snap
    │       │   │       ├── RegisterForm.test.jsx.snap
    │       │   │       ├── UserStatus.test.jsx.snap
    │       │   │       └── UsersList.test.jsx.snap
    │       │   └── form.css
    │       ├── index.js
    │       └── setupTests.js
    ├── nginx
    │   └── default.conf
    └── users
        ├── .coveragerc
        ├── .dockerignore
        ├── Dockerfile
        ├── entrypoint.sh
        ├── manage.py
        ├── project
        │   ├── __init__.py
        │   ├── api
        │   │   ├── __init__.py
        │   │   ├── auth.py
        │   │   ├── ping.py
        │   │   └── users
        │   │       ├── __init__.py
        │   │       ├── admin.py
        │   │       ├── models.py
        │   │       └── views.py
        │   ├── config.py
        │   ├── db
        │   │   ├── Dockerfile
        │   │   └── create.sql
        │   └── tests
        │       ├── __init__.py
        │       ├── conftest.py
        │       ├── pytest.ini
        │       ├── test_admin.py
        │       ├── test_auth.py
        │       ├── test_config.py
        │       ├── test_ping.py
        │       ├── test_user_model.py
        │       ├── test_users.py
        │       └── utils.py
        ├── requirements.txt
        └── setup.cfg

Update the README:

# Deploying a Flask and React Microservice to AWS ECS

HTTPie

Finally, test all the users routes with HTTPie.

GET all users:

$ http GET http://localhost:5001/users

HTTP/1.0 200 OK
Content-Length: 426
Content-Type: application/json
Date: Mon, 05 Aug 2019 04:33:07 GMT
Server: Werkzeug/0.15.5 Python/3.7.4

{
    "data": {
        "users": [
            {
                "active": true,
                "email": "[email protected]",
                "id": 1,
                "username": "michael"
            },
            {
                "active": true,
                "email": "[email protected]",
                "id": 2,
                "username": "michaelherman"
            }
        ]
    },
    "status": "success"
}

GET single user:

$ http GET http://localhost:5001/users/1

HTTP/1.0 200 OK
Content-Length: 159
Content-Type: application/json
Date: Mon, 05 Aug 2019 04:33:33 GMT
Server: Werkzeug/0.15.5 Python/3.7.4

{
    "data": {
        "active": true,
        "email": "[email protected]",
        "id": 1,
        "username": "michael"
    },
    "status": "success"
}

POST:

$ http --json POST http://localhost:5001/users username=someone email=[email protected]

HTTP/1.0 201 CREATED
Content-Length: 79
Content-Type: application/json
Date: Mon, 05 Aug 2019 04:36:05 GMT
Server: Werkzeug/0.15.5 Python/3.7.4

{
    "message": "[email protected] was added!",
    "status": "success"
}

PUT:

$ http --json PUT http://localhost:5001/users/3 username=foo email=[email protected]

HTTP/1.0 200 OK
Content-Length: 61
Content-Type: application/json
Date: Mon, 05 Aug 2019 04:36:47 GMT
Server: Werkzeug/0.15.5 Python/3.7.4

{
    "message": "3 was updated!",
    "status": "success"
}

DELETE:

$ http DELETE http://localhost:5001/users/3

HTTP/1.0 200 OK
Content-Length: 71
Content-Type: application/json
Date: Mon, 05 Aug 2019 04:37:28 GMT
Server: Werkzeug/0.15.5 Python/3.7.4

{
    "message": "[email protected] was removed!",
    "status": "success"
}

GitHub

Create a new project on GitHub and update the Git remote. Push your project up to GitHub once done.




Mark as Completed