Skip to content

artiz/beobachter

Repository files navigation

Beobachter [FastAPI + React]

build

fastapi-logo react-logo sql-alchemy react-logo react-logo

Backend/scripts based on cookiecutter fastapi-react

Features

  • FastAPI with Python 3.9, totally asynchronous
  • React 17
  • create-react-app with Typescript
  • Postgres
  • Redis (PubSub and TimeSeries)
  • Celery + Beat
  • SQLAlchemy with Alembic for migrations and asynchronous I/O (asyncio) support
  • Pytest for backend tests
  • Docker compose for easier development
  • Nginx as a reverse proxy to allow backend and frontend on the same port

Async SQLAlchemy

Parallel database queries with synchronous access

➜  ~ wrk -c30 -t3 -d10s  -H "Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnRlbS5rdXN0aWtvdkBnbWFpbC5jb20iLCJ1aWQiOjUsImZuIjpudWxsLCJsbiI6bnVsbCwicGVybWlzc2lvbnMiOiJ1c2VyIiwiZXhwIjoxNjU0MDIxODg2fQ.439CjqvKtBMvIXBEmH0FLW98Te51ur-VBlTsaS7AkhI" http://localhost:8888/api/users/me  --timeout 5
Running 10s test @ http://localhost:8888/api/users/me
  3 threads and 30 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    49.86ms   23.51ms 167.93ms   67.88%
    Req/Sec   201.13     28.65   343.00     71.00%
  6013 requests in 10.01s, 1.18MB read
Requests/sec:    600.77
Transfer/sec:    121.03KB

Parallel database queries with asynchronous access

 wrk -c500 -t25 -d10s   -H "Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbkBmYXN0YXBpLXJlYWN0LXByb2plY3QuY29tIiwidWlkIjoxLCJmbiI6IkFkbWluIiwibG4iOm51bGwsInBlcm1pc3Npb25zIjoiYWRtaW4iLCJleHAiOjE2NTQwMzQxOTd9.ivCnw0uwce81JdxV7ZHMtl38jVaHUIoD2G95791P634"  http://localhost:8888/api/users/me
Running 10s test @ http://localhost:8888/api/users/me
  25 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   585.91ms  509.32ms   1.99s    79.50%
    Req/Sec    30.16     17.33   160.00     61.57%
  7098 requests in 10.07s, 1.59MB read
  Socket errors: connect 0, read 0, write 0, timeout 316
Requests/sec:    704.59
Transfer/sec:    161.25KB

Development

The only dependencies for this project are docker and docker-compose.

Quick Start

Starting the project with hot-reloading enabled (the first time it will take a while):

docker-compose run backend alembic upgrade head
docker-compose run backend python app/initial_data.py
docker-compose up -d

And navigate to http://localhost:8000

Note: If you see an Nginx error at first with a 502: Bad Gateway page, you may have to wait for webpack to build the development server (the nginx container builds much more quickly).

Auto-generated docs will be at http://localhost:8000/api/docs

Rebuilding containers:

docker-compose build

Restarting containers:

docker-compose restart

Bringing containers down:

docker-compose down --remove-orphans

Force stop and remove all containers:

docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)

Frontend Tests

cd frontend
npm install
npm test

Migrations

Migrations are run using alembic. To run all migrations and load init data:

docker-compose run backend alembic upgrade head
docker-compose run backend python app/initial_data.py

To create a new migration:

alembic revision -m "create users table"

And fill in upgrade and downgrade methods. For more information see Alembic's official documentation.

Testing

There is a helper script for both frontend and backend tests:

./scripts/test.sh

Backend Tests

docker-compose run backend pytest

any arguments to pytest can also be passed after this command

Frontend Tests

docker-compose run frontend npm run test:all

This is the same as running npm test from within the frontend directory

Project Layout

backend
└── app
    ├── alembic
    │   └── versions # where migrations are located
    ├── api
    │   └── base
    │       └── endpoints
    │   └── v2
    │       └── endpoints
    ├── core    # config
    ├── db      # db models
    ├── tests   # pytest
    └── main.py # entrypoint to backend

frontend
└── public
└── src
    ├── components
    │   └── Home.tsx
    ├── config
    │   └── index.tsx   # constants
    ├── __tests__
    │   └── test_home.tsx
    ├── index.tsx   # entrypoint
    └── App.tsx     # handles routing