The main goal of this little demonstration project is to explore FastAPI framework
using asyncio WITH a higher level abstraction
named databases connected to a SQL (not async io by nature) PostgreSQL database.
This project is very much inspired by the tutorial of the databases framework itself that you can find
at 'databases' QuickStart
which gives asyncio support for a range of SQL databases.
Enjoy 🍺 !
Note
If you are installing Python on Windows, be sure to check the box to have
Python added to your PATH if the installer offers such an option (it's
normally off by default).
This project use the marvelous Poetry package manager and builder.
It handles dependencies management, locking version and publishing to pypi automagically.
It eases your day to day Python development projects with a lot a easy command to install a virtual environment and
manage dependencies in a very secured way.
$ make help
Wed Dec 3023:35:52 CET 2020# # __ __ ______ __ ______ # /\ \_\ \ /\ ___\ /\ \ /\ == \ # \ \ __ \ \ \ __\ \ \ \____ \ \ _-/ # \ \_\ \_\ \ \_____\ \ \_____\ \ \_\ # \/_/\/_/ \/_____/ \/_____/ \/_/ # # ---------------HELP------------------------------------# - Setup the project : make install# - Run the server (blocking) : make start# - Evaluate status of the server : make status# - Stop (kill) the server : make kill# - Tail the current log file : make tail# - Run tests on a running server : make tests# - Run tests on a running server : make load_tests# - Launch mkdocs server Run : make serve# - Publish docs GitHub pages : make publish# --------------------------------------------------------
Enjoy 🍺 !
If you don't have any local postgresql instance on your local machine, you can use the
provided docker-compose stack.
Of course, you'll need to choose the appropriate docker installation (you should install the proper docker desktop
for your local OS) and install the docker-compose utility tool as well
Note
Please refer to the official documentations of docker and docker-compose for your environment.
$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------
fastapi-async-with-postgresql_adminer_1 entrypoint.sh docker-php-e ... Up 0.0.0.0:8080->8080/tcp
fastapi-async-with-postgresql_db_1 docker-entrypoint.sh postgres Up 0.0.0.0:5432->5432/tcp
$ make run
2020-12-30 10:17:42,350:INFO:db_utils:Defining configuration for db at [postgresql://vincent:****@localhost:5432/vincent?sslmode=prefer]2020-12-30 10:17:42,423:INFO:schema:Defining table 'notes'2020-12-30 10:17:42,424:INFO:schema:Creating schema
2020-12-30 10:17:42,614 INFO sqlalchemy.engine.base.Engine select version()2020-12-30 10:17:42,614:INFO:sqlalchemy.engine.base.Engine:select version()2020-12-30 10:17:42,614 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:42,614:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:42,616 INFO sqlalchemy.engine.base.Engine select current_schema()2020-12-30 10:17:42,616:INFO:sqlalchemy.engine.base.Engine:select current_schema()2020-12-30 10:17:42,617 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:42,617:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:42,619 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:42,619:INFO:sqlalchemy.engine.base.Engine:SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:42,619 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:42,619:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:42,620 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:42,620:INFO:sqlalchemy.engine.base.Engine:SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:42,620 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:42,620:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:42,620 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings
2020-12-30 10:17:42,620:INFO:sqlalchemy.engine.base.Engine:show standard_conforming_strings
2020-12-30 10:17:42,621 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:42,621:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:42,622 INFO sqlalchemy.engine.base.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s
2020-12-30 10:17:42,622:INFO:sqlalchemy.engine.base.Engine:select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s
2020-12-30 10:17:42,622 INFO sqlalchemy.engine.base.Engine {'name': 'notes'}2020-12-30 10:17:42,622:INFO:sqlalchemy.engine.base.Engine:{'name': 'notes'}
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)2020-12-30 10:17:42,639:INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
INFO: Started reloader process [8791] using statreload
2020-12-30 10:17:42,639:INFO:uvicorn.error:Started reloader process [8791] using statreload
2020-12-30 10:17:43,137:INFO:db_utils:Defining configuration for db at [postgresql://vincent:****@localhost:5432/vincent?sslmode=prefer]2020-12-30 10:17:43,228:INFO:schema:Defining table 'notes'2020-12-30 10:17:43,233:INFO:schema:Creating schema
2020-12-30 10:17:43,300 INFO sqlalchemy.engine.base.Engine select version()2020-12-30 10:17:43,300:INFO:sqlalchemy.engine.base.Engine:select version()2020-12-30 10:17:43,300 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:43,300:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:43,305 INFO sqlalchemy.engine.base.Engine select current_schema()2020-12-30 10:17:43,305:INFO:sqlalchemy.engine.base.Engine:select current_schema()2020-12-30 10:17:43,306 INFO sqlalchemy.engine.base.Engine {}2020-12-30 10:17:43,306:INFO:sqlalchemy.engine.base.Engine:{}2020-12-30 10:17:43,309 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:43,309:INFO:sqlalchemy.engine.base.Engine:SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2020-12-30 10:17:43,309 INFO sqlalchemy.engine.base.Engine {}
poetry run python src/main.py
Python 2.7 will no longer be supported in the next feature release of Poetry (1.2).
You should consider updating your Python version to a supported one.
Note that you will still be able to manage Python 2.7 projects by using the env command.
See https://python-poetry.org/docs/managing-environments/ for more information.
The currently activated Python version 2.7.16 is not supported by the project (^3.9).
Trying to find and use a compatible version.
Using python3 (3.9.1)2020-12-29 21:23:39,115:INFO:db_utils:Defining configuration for db at [postgresql://vincent:****@localhost:5432/vincent?sslmode=prefer]2020-12-29 21:23:39,145:INFO:schema:Defining table 'notes'2020-12-29 21:23:39,146:INFO:schema:Creating schema
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)2020-12-29 21:23:39,200:INFO:uvicorn.error:Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
INFO: Started reloader process [47773] using statreload
2020-12-29 21:23:39,200:INFO:uvicorn.error:Started reloader process [47773] using statreload
2020-12-29 21:23:39,458:INFO:db_utils:Defining configuration for db at [postgresql://vincent:****@localhost:5432/vincent?sslmode=prefer]2020-12-29 21:23:39,484:INFO:schema:Defining table 'notes'2020-12-29 21:23:39,485:INFO:schema:Creating schema
INFO: Started server process [47780]2020-12-29 21:23:39,541:INFO:uvicorn.error:Started server process [47780]
INFO: Waiting for application startup.
2020-12-29 21:23:39,542:INFO:uvicorn.error:Waiting for application startup.
2020-12-29 21:23:39,559:INFO:databases:Connected to database postgresql://vincent:********@localhost:5432/vincent?sslmode=prefer
INFO: Application startup complete.
2020-12-29 21:23:39,560:INFO:uvicorn.error:Application startup complete.
^CINFO: Shutting down
2020-12-30 10:26:11,615:INFO:uvicorn.error:Shutting down
INFO: Waiting for application shutdown.
2020-12-30 10:26:11,718:INFO:uvicorn.error:Waiting for application shutdown.
2020-12-30 10:26:11,728:INFO:databases:Disconnected from database postgresql://vincent:********@localhost:5432/vincent?sslmode=prefer
INFO: Application shutdown complete.
2020-12-30 10:26:11,730:INFO:uvicorn.error:Application shutdown complete.
INFO: Finished server process [8797]2020-12-30 10:26:11,730:INFO:uvicorn.error:Finished server process [8797]
INFO: Stopping reloader process [8791]2020-12-30 10:26:11,895:INFO:uvicorn.error:Stopping reloader process [8791]
Once you have your database up and running you should be able to launche all the test with one command with the help of
the Makefile command make tests.
[...]2020-12-30T20:43:03Z <Greenlet at 0x1117bf6a0: run_user(<locustfile.QuickstartUser object at 0x111817ca0>)> failed with KeyboardInterrupt
[2020-12-30 21:43:03,026] yourmachine.local/INFO/locust.main: Running teardowns...
[2020-12-30 21:43:03,026] yourmachine.local/INFO/locust.main: Shutting down (exit code 1), bye.
[2020-12-30 21:43:03,026] yourmachine.local/INFO/locust.main: Cleaning up runner...
[2020-12-30 21:43:03,026] yourmachine.local/INFO/locust.runners: Stopping 2 users
[2020-12-30 21:43:03,027] yourmachine.local/INFO/locust.runners: 2 Users have been stopped, 0 still running
Name # reqs # fails | Avg Min Max Median | req/s failures/s
--------------------------------------------------------------------------------------------------------------------------------------------
POST /notes 20(0.00%)|19142315|0.03 0.00
GET /notes/1 28062806(100.00%)|43174|45.28 45.28
GET /notes/10 28042804(100.00%)|43154|45.25 45.25
GET /notes/2 28060(0.00%)|43124|45.28 0.00
GET /notes/3 28060(0.00%)|43294|45.28 0.00
GET /notes/4 28060(0.00%)|43134|45.28 0.00
GET /notes/5 28060(0.00%)|43154|45.28 0.00
GET /notes/6 280523(0.82%)|43104|45.26 0.37
GET /notes/7 28052805(100.00%)|43114|45.26 45.26
GET /notes/8 28042804(100.00%)|43154|45.25 45.25
GET /notes/9 28042804(100.00%)|43264|45.25 45.25
--------------------------------------------------------------------------------------------------------------------------------------------
Aggregated 2805414046(50.07%)|43294|452.69 226.65
Response time percentiles (approximated)
Type Name 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 100% # reqs
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
POST /notes 23232323232323232323232
GET /notes/1 444555671017172806
GET /notes/10 444555671215152804
GET /notes/2 444555671013132806
GET /notes/3 444555671230302806
GET /notes/4 444555661314142806
GET /notes/5 444555671416162806
GET /notes/6 44455567911112805
GET /notes/7 44455567912122805
GET /notes/8 444555671116162804
GET /notes/9 444555671026262804
--------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
None Aggregated 4445556710233028054
Error report
# occurrences Error
--------------------------------------------------------------------------------------------------------------------------------------------
2806 GET /notes/1: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/1')23 GET /notes/6: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/6')2805 GET /notes/7: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/7')2804 GET /notes/8: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/8')2804 GET /notes/9: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/9')2804 GET /notes/10: HTTPError('404 Client Error: Not Found for url: http://localhost:5000/notes/10')
--------------------------------------------------------------------------------------------------------------------------------------------
make: *** [load_tests] Error 1
You can use make serve to start the documentation server :
1
2
3
4
5
6
7
8
9
10
11
12
$ make serve
INFO - Building documentation...
INFO - Cleaning site directory
INFO - Documentation built in 0.10 seconds
[I 20123022:19:08 server:335] Serving on http://127.0.0.1:8000
INFO - Serving on http://127.0.0.1:8000
[I 20123022:19:08 handlers:62] Start watching changes
INFO - Start watching changes
[I 20123022:19:08 handlers:64] Start detecting changes
INFO - Start detecting changes
[I 20123022:19:11 handlers:135] Browser Connected: http://127.0.0.1:8000/#objectives
INFO - Browser Connected: http://127.0.0.1:8000/#objectives