https://github.com/andgineer/api-db-prototype
API with SQL Alchemy layer alternatively implemented with Flask and Connexion
https://github.com/andgineer/api-db-prototype
alembic connexion flask flask-login jwt sqlalchemy
Last synced: about 2 months ago
JSON representation
API with SQL Alchemy layer alternatively implemented with Flask and Connexion
- Host: GitHub
- URL: https://github.com/andgineer/api-db-prototype
- Owner: andgineer
- Created: 2018-11-06T15:28:08.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2025-02-18T16:57:38.000Z (over 1 year ago)
- Last Synced: 2025-02-18T17:38:02.521Z (over 1 year ago)
- Topics: alembic, connexion, flask, flask-login, jwt, sqlalchemy
- Language: Python
- Homepage:
- Size: 1.22 MB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[](https://github.com/andgineer/api-db-prototype/actions)
[](https://htmlpreview.github.io/?https://github.com/andgineer/api-db-prototype/blob/python-coverage-comment-action-data/htmlcov/index.html)
# API server experiment
This project is an experimental implementation of an API server using different approaches.
## Approaches
1. **Connexion - Spec First**
2. **Flask - Code First**
In both cases, we use SQLAlchemy for DB access and Alembic for DB versioning, implement JWT auth,
and provide Swagger UI.
## Common Controllers
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.
## Real-World Considerations
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. I would prefer 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 depends on various factors, including project size, development team size,
and the requirement to implement an external API specification.
# Run dev version (Flask Werkzeug) with auto reload on source change
. ./activate.sh # Build and/or activate virtual environment
make run # Run dev server
# API UI
[Swagger UI](http://127.0.0.1:5000/ui)
### To authorize API calls:
- Execute from the UI API request `Users` -> `Auth` -> `Try it`
- email `admin@`, password `admin`
- press `Execute`
- copy the token from the response
- On top of the page press green button with lock icon `Authorize`
- paste in the `Value` the token
- press `Authorize` and after that `Close`
After that you can send all the API requests (`Try it out` button).
Swagger API will automatically add the security token.
# Folders overview
* `api` - Swagger (Open API) description of the API
* `src/db` - SQLAlchemy models
* `src/alembic` - DB metadata versioning
* `src/controllers` - Application logic common for Flask and Connexion
* `src/flask_server` - HTTP server (request routing to application logic)
* `src/openapi_server` - Auto-generated with `make codegen`.
`controllers/` and `encoder.py` are manually modified.
* `src/settings.py` - Configs for test/dev/prod
* `src/build_timestamp` - Autogenerated file with last git commit timestamp to use as 'build' time - see `make git-hook-install`
* `src/secret` - Key and certificate for signing and verification of security tokens (`jwt_token.py`)
* `src/tests` - Pytest tests, to run use `make test`
* `src/app.py` - ASGI/WSGI app
* `src/journaling.py` - Central journaling settings
* `src/jwt_token.py` - Security tokens
* `src/password_hash.py` - Password hashing
* `src/config.py` - Config loader, is not used in this project
# Scripts
make help
# Development
## Virtual environment
Use `. ./activate.sh` to create and/or activate.
And `deactivate` to exit.
To upgrade python packages in the virtual environment use `make reqs`.
## DB
The project uses SQLAlchemy and Alembic for DB access and versioning.
To create objects in an 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
## API development
To add new requests to the API, first implement controllers for them in `src/controllers/`.
Next, we use different approaches for Flask and Connexion.
### Flask
Implement proxies for the requests in `src/flask_server/api_app.py`.
### Connexion
Add new requests to the API spec in `api/swagger.yaml`.
Run `make codegen` to generate new code in `src/openapi_server/`.
Unfortunately, this can break some manual changes - for example, I had to
modify `src/openapi_server/encoder.py`. In most cases, you can just rollback unwanted changes.
Next, implement proxies for the requests in `src/openapi_server/controllers`
using signatures from auto-generated `src/openapi_server/controllers_boilerplate`.
## Libraries
* [Flask](https://flask.palletsprojects.com/en/3.0.x/)
* [Flask OpenAPI UI](https://github.com/sveint/flask-swagger-ui)
* [Connexion](https://connexion.readthedocs.io/en/latest/index.html)
* DB [SQLAlchemy](http://wiki.python.su/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D0%B8/SQLAlchemy)
* [alembic](https://pypi.org/project/alembic/) for DB metadata versioning
* [flask-login](https://flask-login.readthedocs.io/en/latest/)
* [jwt](https://realpython.com/token-based-authentication-with-flask/)
## Open API (Swagger) spec
You can convert the Swagger file `api/swagger.yaml` into a document at
https://editor.swagger.io or in the AWS console - `Amazon AWS API Gateway`.
Visualization is also available on [Swagger Hub](https://app.swaggerhub.com/apis/andgineer/api-db-prototype/1.0-oas3)
## Configs
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).
## JWT keys
For web token crypto, the server uses keys from files configured in the config
object.
The default is `secret/`.
For an example of how to recreate keys, see `create_keys.sh`.
The private key is for token issuing.
If the web application gets tokens from an external service
like Amazon Cognito, you should provide only the public key from that
external service, so our server can check this external service's tokens.
The public key is expected in `pem` certificate format.
## Python version
At least 3.10 because we use [Concatenate](https://peps.python.org/pep-0612/)
# Production
In production, you should use production-ready servers like
Gunicorn or uWSGI.
[WSGI for Flask](https://flask.palletsprojects.com/en/3.0.x/deploying/) and
[ASGI for Connexion](https://connexion.readthedocs.io/en/latest/quickstart.html#running-your-application).
See example in `prod.sh` and `Dockerfile`.
## Coverage report
* [Codecov](https://app.codecov.io/gh/andgineer/api-db-prototype/tree/master/src)
* [Coveralls](https://coveralls.io/github/andgineer/api-db-prototype)