This project is an experimental implementation of an API server using different approaches.
- Connexion - Spec First
- Flask - Code First
In both cases we use SQLAlchemy for DB access and Alembic for DB versioning, implement JWT auth. And provide Swagger UI.
In this project, we're trying to create controllers (application logic) that can be shared between the two different approaches we're using.
However, because of the way Connexion generates code, we can't use application objects in the controllers parameters. We have to keep their signatures a bit more generic to work with both approaches.
In a real-world project, it is advisable to choose a single approach and use it consistently.
Frameworks like FastAPI and LiteStar utilize Python type hinting to automatically generate API specifications. And I would prefere them over Flask.
In contrast, Connexion generates Python code from API specs, requiring you to develop the API specification first.
The choice between these approaches can depend on various factors, including project size, development team size, and the requirement to implement external API specification.
. ./activate.sh # build and/or activate virtual environment
make run # run dev server
- Execute from the UI API request
Users
->Auth
->Try it
- email
admin@
, passwordadmin
- press
Execute
- copy the token from the response
- email
- On top of the page press green button with lock icon
Authorize
- paste in the
Value
the token - press
Authorize
and after thatClose
- paste in the
After that you can send all the API requests (Try it out
button).
Swagger API will automatically add the security token.
api
- Swagger (Open API) description of the APIsrc/db
- SQLAlchemy modelssrc/alembic
- DB metadata versioningsrc/controllers
- Application logic common for Flask and Connexionsrc/flask_server
- HTTP server (request routing to application logic)src/openapi_server
- Auto-generated withmake codegen
.controllers/
andencoder.py
are manually modified.src/settings.py
- Configs for test/dev/prodsrc/biuld_timestamp
- Autogenerated file with last git commit timestamp to use as 'build' time - seemake git-hook-install
src/secret
- Key and certificate for signing and verification of security tokens (jwt_token.py
)src/tests
- pytests, to run usemake test
src/app.py
- ASGI/WSGI appsrc/journaling.py
- Central journaling settingssrc/jwt_token.py
- Security tokenssrc/password_hash.py
- Password hashingsrc/config.py
- Config loader, is not used in this project
make help
Use . ./activate.sh
to create and/or activate.
And deactivate
to exit.
To upgrade python packages in the virtual environment use make reqs
.
The project uses SQLAlchemy and Alembic for DB access and versioning.
To create objects in empty DB
make db-create
The DB connect string is in src/settings.py
.
Other DB-related commands
make db-upgrade
make db-show-migration
make db-migration
To add new requests to the API, first you should implement controllers for them in src/controllers/
.
Next we use different approaches for Flask and Connexion.
Implement proxies for the requests in src/flask_server/api_app.py
.
Add new requests to the API spec in api/swagger.yaml
.
Run make codegen
to generate new code in src/openapi_server/
.
Unfortunately it can brake some manual changes - for example I had to
modify src/openapi_server/encoder.py
. In most cases you can just rollback unwanted changes.
Next you should implement proxies for the requests in src/openapi_server/controllers
using signatures from autogenerated src/openapi_server/controllers_boilerplate
.
- Flask
- Flask OpenAPI UI
- Connexion
- DB SQLAlchemy
- alembic for DB metadata versioning
- flask-login
- jwt
You can convert the Swagger file api/swagger.yaml
into document at
https://editor.swagger.io or in AWS console - Amazon AWS API Gateway
.
Visualization also available on Swagger HUB
See settings.py
.
For Prod
config you should specify DB in settings.py
.
This config would be used by default (if no SERVER_ENV
specified).
For web token crypto server uses keys from files configured in the config
object.
Default is secret/
.
Example how to recreate keys see in create_keys.sh
.
Private key is for token issuing.
If the web application would get tokens from external service like Amazon Cognito, you should provide only public key from that external service, so our server could check this external service's tokens.
Public key is expected in pem
certificate format.
At least 3.10 because we use Concatenate
In production you should use production-ready servers like Gunicorn or uWsgi.
WSGI for Flask and ASGI for Connection.
See example in prod.sh
and Dockerfile
.