{"id":26513634,"url":"https://github.com/khteh/pythonrestapi","last_synced_at":"2025-10-08T15:51:36.913Z","repository":{"id":39825057,"uuid":"268266598","full_name":"khteh/PythonRestAPI","owner":"khteh","description":"Python RestAPI using Quart web microframework and served using Hypercorn ASGI and WSGI web server.","archived":false,"fork":false,"pushed_at":"2025-07-13T04:40:21.000Z","size":5264,"stargazers_count":4,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-13T06:32:38.064Z","etag":null,"topics":["asgi","asgi-server","gemini","http3-server","hypercorn","llm","python3","quart","quic","wsgi-server"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/khteh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-05-31T11:32:43.000Z","updated_at":"2025-07-13T04:40:24.000Z","dependencies_parsed_at":"2022-08-23T02:50:30.402Z","dependency_job_id":"0c06ccf5-0aec-4789-9492-9f21189796ca","html_url":"https://github.com/khteh/PythonRestAPI","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/khteh/PythonRestAPI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khteh%2FPythonRestAPI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khteh%2FPythonRestAPI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khteh%2FPythonRestAPI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khteh%2FPythonRestAPI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khteh","download_url":"https://codeload.github.com/khteh/PythonRestAPI/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khteh%2FPythonRestAPI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278972330,"owners_count":26078017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asgi","asgi-server","gemini","http3-server","hypercorn","llm","python3","quart","quic","wsgi-server"],"created_at":"2025-03-21T04:29:09.927Z","updated_at":"2025-10-08T15:51:36.907Z","avatar_url":"https://github.com/khteh.png","language":"Python","readme":"# Python Quart RestAPI\n\nPython RestAPI using Quart HTTP/3 ASGI framework.\n\n## Environment setup\n\n- On a development host, create a virtual environment for development and test. Both must be using the same virtual environment.\n- Virtual environment is optional for CI/CD do because everything runs in a docker container.\n- Add the following to `.env` for database access credentials:\n\n```\nDB_USERNAME=username\nDB_PASSWORD=password\n```\n\n### Google Gemini API\n\n- Obtain an API key from https://aistudio.google.com/apikey\n- Add a `.env` file with the following content:\n  ```\n  GEMINI_API_KEY=\u003capi key\u003e\n  ```\n\n### Clean up pipenv\n\n- `pipenv --rm`\n- `pipenv --clear`\n- `pipenv lock --clear --verbose`\n\n### Setup new pipenv\n\n```\n$ pipenv install\n$ pipenv shell\n```\n\n## Database setup:\n\n- This project uses PostgreSQL database with SQLAlchemy ORM with marshmallow for object SerDes.\n\n## Install python modules\n\n```\n$ pipenv install --python=/path/to/python\n$ cd src\n$ pipenv install --python=/path/to/python\n$ cd test\n$ pipenv install --python=/path/to/python\n```\n\n## Create Database\n\n- Firstly, create an empty database \"library\" in PostgreSQL\n\n## Database Migration\n\n- Copy `env.py` to `migrations/` folder.\n- Set the values `DB_foo` in `/etc/pythonrestapi_config.json`\n- run migrations initialization with db init command:\n\n  ```\n  $ pipenv run alembic init migrations\n  $ cp env.py migrations\n  $ pipenv run alembic revision --autogenerate -m \"Initial migration\"\n  $ pipenv run alembic upgrade head\n  ```\n\n- There will be 3 tables, \"users\", \"books\", and \"authors\" created in the PostgreSQL database \"library\" after the `upgrade`.\n\n## Test using PyTest:\n\n- There are 7 test cases\n  - JWT token generation and decoding\n  - HomeController\n  - FibonacciController\n  ```\n  $ pipenv run pytest -v\n  $ python -m pytest\n  ```\n\n## Continuous Integration:\n\n- Integrated with CircleCI\n\n## Start the application:\n\n- `./hypercorn.sh`\n\n## HTTP/3 curl:\n\n- To build your own HTTP/3 curl: https://curl.se/docs/http3.html\n- Add `Host` header which is defined as `server_names` in `hypercorn.toml`\n\n```\ncurl --http3-only --insecure -v https://localhost:4433/\u003cpath\u003e -H \"Host: khteh.com\"\n```\n\n```\ncurl --http3-only --insecure -vv https://localhost:4433/chat/invoke -F 'prompt=:\"What is task decomposition?\"' -F 'file=@~/data/1.jpg' -F 'receipt=true'\n```\n\n## Chrome browser\n\n- Close ALL chrome browser (both tabs and windows)\n\n- Generate TLS certificate fingerprint:\n\n```\n$ fingerprint=`openssl x509 -pubkey -noout -in /tmp/server.crt |\n        openssl rsa -pubin -outform der |\n        openssl dgst -sha256 -binary | base64`\n```\n\n- Start Chrome browser with QUIC protocol for HTTP/3:\n- `$1` is URL. For example: `https://localhost:4433`\n\n```\n$  /opt/google/chrome/chrome --disable-setuid-sandbox --enable-quic --ignore-certificate-errors-spki-list=$fingerprint --origin-to-force-quic-on=${1#*//} $1\n```\n\n## Use Cases\n\n### Google Gemini API\n\n- Return text message from input prompt string by calling the API\n- Return text message from input prompt string and an image file by calling the API\n- Multimodal:\n  - Upload an image, \"receipt\" in this case, and it wil return a structured data containing details in the receipt:\n  ```\n  2025-05-21 13:15:34 DEBUG    ProcessReceipt response: {\n  \"date_str\": \"28-07-2017\",\n  \"vendor\": \"Walmart\",\n  \"currency\": \"USD\",\n  \"items\": [\n    {\n      \"name\": \"PET TOY\",\n      \"amount\": 1.97\n    },\n    {\n      \"name\": \"FLOPPY PUPPY\",\n      \"amount\": 1.97\n    },\n    {\n      \"name\": \"SSSUPREME S\",\n      \"amount\": 4.97\n    },\n    {\n      \"name\": \"2.5 SQUEAK\",\n      \"amount\": 5.92\n    },\n    {\n      \"name\": \"MUNCHY DMBEL\",\n      \"amount\": 3.77\n    },\n    {\n      \"name\": \"DOG TREAT\",\n      \"amount\": 2.92\n    },\n    {\n      \"name\": \"PED PCH 1\",\n      \"amount\": 0.50\n    },\n    {\n      \"name\": \"PED PCH 1\",\n      \"amount\": 0.50\n    },\n    {\n      \"name\": \"COUPON 23100\",\n      \"amount\": 1.00\n    },\n    {\n      \"name\": \"HNYMD SMORES\",\n      \"amount\": 3.98\n    },\n    {\n      \"name\": \"FRENCH DRSNG\",\n      \"amount\": 1.98\n    },\n    {\n      \"name\": \"3 ORANGES\",\n      \"amount\": 5.47\n    },\n    {\n      \"name\": \"BABY CARROTS\",\n      \"amount\": 1.48\n    },\n    {\n      \"name\": \"COLLARDS\",\n      \"amount\": 1.24\n    },\n    {\n      \"name\": \"CALZONE\",\n      \"amount\": 2.50\n    },\n    {\n      \"name\": \"MM RVW MNT\",\n      \"amount\": 19.77\n    },\n    {\n      \"name\": \"STKOBRLPLABL\",\n      \"amount\": 1.97\n    },\n    {\n      \"name\": \"STKOBRLPLABL\",\n      \"amount\": 1.97\n    },\n    {\n      \"name\": \"STKO SUNFLWR\",\n      \"amount\": 0.97\n    },\n    {\n      \"name\": \"STKO SUNFLWR\",\n      \"amount\": 0.97\n    },\n    {\n      \"name\": \"STKO SUNFLWR\",\n      \"amount\": 0.97\n    },\n    {\n      \"name\": \"STKO SUNFLWR\",\n      \"amount\": 0.97\n    },\n    {\n      \"name\": \"BLING BEADS\",\n      \"amount\": 0.97\n    },\n    {\n      \"name\": \"GREAT VALUE\",\n      \"amount\": 9.97\n    },\n    {\n      \"name\": \"LIPTON\",\n      \"amount\": 4.48\n    },\n    {\n      \"name\": \"DRY DOG\",\n      \"amount\": 12.44\n    }\n  ],\n  \"tax\": 4.59,\n  \"total\": 98.21\n  }, receipt: vendor='Walmart' currency='USD' items=[ReceiptItem(name='PET TOY', amount=1.97), ReceiptItem(name='FLOPPY PUPPY', amount=1.97), ReceiptItem(name='SSSUPREME S', amount=4.97), ReceiptItem(name='2.5 SQUEAK', amount=5.92), ReceiptItem(name='MUNCHY DMBEL', amount=3.77), ReceiptItem(name='DOG TREAT', amount=2.92), ReceiptItem(name='PED PCH 1', amount=0.5), ReceiptItem(name='PED PCH 1', amount=0.5), ReceiptItem(name='COUPON 23100', amount=1.0), ReceiptItem(name='HNYMD SMORES', amount=3.98), ReceiptItem(name='FRENCH DRSNG', amount=1.98), ReceiptItem(name='3 ORANGES', amount=5.47), ReceiptItem(name='BABY CARROTS', amount=1.48), ReceiptItem(name='COLLARDS', amount=1.24), ReceiptItem(name='CALZONE', amount=2.5), ReceiptItem(name='MM RVW MNT', amount=19.77), ReceiptItem(name='STKOBRLPLABL', amount=1.97), ReceiptItem(name='STKOBRLPLABL', amount=1.97), ReceiptItem(name='STKO SUNFLWR', amount=0.97), ReceiptItem(name='STKO SUNFLWR', amount=0.97), ReceiptItem(name='STKO SUNFLWR', amount=0.97), ReceiptItem(name='STKO SUNFLWR', amount=0.97), ReceiptItem(name='BLING BEADS', amount=0.97), ReceiptItem(name='GREAT VALUE', amount=9.97), ReceiptItem(name='LIPTON', amount=4.48), ReceiptItem(name='DRY DOG', amount=12.44)] tax=4.59 total=98.21 date_of_receipt=datetime.date(2017, 7, 28)\n  ```\n  - On the UI:\n    ![Process Receipt](./ProcessReceipt.png?raw=true \"Process Receipt\")\n\n### Create User:\n\n- POST https://localhost:4433/users/create with the following JSON data:\n  ```\n  {\n      \"firstname\": \"First Name\",\n      \"lastname\": \"LastName\",\n      \"email\": \"firstname.lastname@email.com\",\n      \"password\": \"P@$$w0rd\"\n  }\n  ```\n\n### Login:\n\n- POST https://localhost:4433/users/login with the following JSON data:\n  ```\n  {\n      \"email\": \"firstname.lastname@email.com\",\n      \"password\": \"P@$$w0rd\"\n  }\n  ```\n- Sample response:\n  ```\n  {\n      \"jwt_token\": \"token string\"\n  }\n  ```\n\n### Subsequent request header:\n\n```\nKey: api-key\nVaue: jwt_token from the login response\n```\n\n### Create Author:\n\n- POST https://localhost:4433/authors/create with the following JSON data:\n  ```\n  {\n      \"email\": \"jk@email.com\",\n      \"firstname\": \"JK\",\n      \"lastname\": \"Rowing\"\n  }\n  ```\n\n### Create Book:\n\n- POST https://localhost:4433/books/create with the following JSON data:\n  ```\n  {\n      \"author_id\": 1,\n      \"isbn\": \"123456\",\n      \"page_count\": \"123\",\n      \"title\": \"My First Book\"\n  }\n  ```\n\n### Delete an author:\n\n- Books table has a foreign key id to Authors.id and this is defined as required in BookSchema.\n- Therefore, when a DELETE RESTful operation is sent to the application to delete an author which has associated book:\n\n```\nmysql.connector.errors.IntegrityError: 1048 (23000): Column 'author_id' cannot be null\n```\n\n### Get Requests:\n\n- Headers:\n\n  ```\n  Key: api-key\n  Vaue: jwt_token from the login response\n  ```\n\n- visit https://localhost:4433\n- visit https://localhost:4433/fibonacci/\n- visit https://localhost:4433/users/all\n- visit https://localhost:4433/authors/all\n- visit https://localhost:4433/books/all\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhteh%2Fpythonrestapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhteh%2Fpythonrestapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhteh%2Fpythonrestapi/lists"}