{"id":19393286,"url":"https://github.com/ruanbekker/flask-api-structured-primer","last_synced_at":"2026-05-05T06:41:10.368Z","repository":{"id":213704548,"uuid":"734731053","full_name":"ruanbekker/flask-api-structured-primer","owner":"ruanbekker","description":"Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc","archived":false,"fork":false,"pushed_at":"2024-01-22T20:06:54.000Z","size":72,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-07T14:56:41.401Z","etag":null,"topics":["docker","flasgger","flask","makefile","mysql","python","sqlalchemy"],"latest_commit_sha":null,"homepage":"https://ruan.dev","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/ruanbekker.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}},"created_at":"2023-12-22T13:08:35.000Z","updated_at":"2025-01-29T04:27:03.000Z","dependencies_parsed_at":"2024-11-10T10:33:43.584Z","dependency_job_id":null,"html_url":"https://github.com/ruanbekker/flask-api-structured-primer","commit_stats":null,"previous_names":["ruanbekker/flask-api-structured-primer"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ruanbekker/flask-api-structured-primer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fflask-api-structured-primer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fflask-api-structured-primer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fflask-api-structured-primer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fflask-api-structured-primer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruanbekker","download_url":"https://codeload.github.com/ruanbekker/flask-api-structured-primer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Fflask-api-structured-primer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274136625,"owners_count":25228412,"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-09-08T02:00:09.813Z","response_time":121,"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":["docker","flasgger","flask","makefile","mysql","python","sqlalchemy"],"created_at":"2024-11-10T10:32:34.970Z","updated_at":"2026-05-05T06:41:10.313Z","avatar_url":"https://github.com/ruanbekker.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flask-api-structured-primer\nStructured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Directory Structure](#directory-structure)\n- [Tests](#tests)\n  - [Static Code Analysis](#static-code-analysis)\n  - [Unit Tests](#unit-tests)\n  - [Code Coverage](#code-coverage)\n  - [Running tests with Docker](#running-tests-with-docker)\n- [Database Migrations](#database-migrations)\n- [API Documentation](#swagger-api-documentation)\n\n## Getting started\n\nTo view the makefile targets:\n\n```bash\nmake\n```\n\nLocal testing:\n\n```bash\nmake test\n```\n\nDocker:\n\n```bash\nmake up\n```\n\nScripts is under `_scripts/` or you can use the makefile targets:\n\n```bash\nmake requests-create\nmake requests-retrieve\n```\n\n## Directory Structure\n\n\u003cdetails\u003e\n  \u003csummary\u003eTree view of the directory structure:\u003c/summary\u003e\n\n```bash\n├── Dockerfile\n├── Makefile\n├── README.md\n├── _scripts\n│   ├── http\n│   │   ├── create.sh\n│   │   ├── delete.sh\n│   │   ├── get.sh\n│   │   ├── list.sh\n│   │   └── update.sh\n│   ├── migrations\n│   │   └── db_migrations.sh\n│   └── tests\n│       ├── lint.sh\n│       └── unit_tests.sh\n├── app.py\n├── config.py\n├── database\n│   └── db.py\n├── docker-compose-jobs.yaml\n├── docker-compose.yaml\n├── migrations\n│   ├── README\n│   ├── alembic.ini\n│   ├── env.py\n│   ├── script.py.mako\n│   └── versions\n├── models\n│   └── product.py\n├── requirements.txt\n├── services\n│   └── product_service.py\n├── shared\n│   └── logging_utils.py\n├── tests\n│   └── test_product_service.py\n└── views\n    ├── healthprobe_views.py\n    └── product_views.py\n\n12 directories, 27 files\n```\n\n\u003c/details\u003e\n\n## Tests\n\n- static code analysis (pylint, prospector, bandit)\n- unit tests (unittest)\n- code coverage (coverage)\n- optional: using docker to run all at once\n\n### Static Code Analysis\n\nTo run linting:\n\n```bash\nfind . -name \"*.py\" ! -path \"./venv/*\" -exec pylint --rcfile .pylintrc --verbose {} +\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003epylint output\u003c/summary\u003e\n\n```bash\nUsing config file .pylintrc\n************* Module shared.logging_utils\nshared/logging_utils.py:58:17: C0303: Trailing whitespace (trailing-whitespace)\nshared/logging_utils.py:48:4: C0103: Variable name \"ch\" doesn't conform to snake_case naming style (invalid-name)\n************* Module views.product_views\nviews/product_views.py:68:4: C0103: Variable name \"e\" doesn't conform to snake_case naming style (invalid-name)\nviews/product_views.py:88:4: C0103: Variable name \"e\" doesn't conform to snake_case naming style (invalid-name)\n\n-------------------------------------------------------------------\nYour code has been rated at 9.86/10 (previous run: 10.00/10, -0.14)\n```\n\nWhen the issues has been fixed:\n\n```bash\n-------------------------------------------------------------------\nYour code has been rated at 10.00/10 (previous run: 9.81/10, +0.19)\n```\n\u003c/details\u003e\n\nRun prospector:\n\n```bash\nprospector --profile .prospector.yaml\n# prospector --strictness low --with-tool pydocstyle\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eprospector output\u003c/summary\u003e\n\n```bash\nCheck Information\n=================\n         Started: 2023-11-19 16:07:29.863667\n        Finished: 2023-11-19 16:07:44.618042\n      Time Taken: 14.75 seconds\n       Formatter: grouped\n        Profiles: .prospector.yaml, doc_warnings, strictness_medium, strictness_high, strictness_veryhigh, no_member_warnings\n      Strictness: from profile\n  Libraries Used: flask\n       Tools Run: dodgy, profile-validator, pycodestyle, pydocstyle, pyflakes, pylint\n  Messages Found: 0\n External Config: pylint: /Users/ruan/personal/eng-python-fastapi-products/.pylintrc\n```\n\nWhen you have errors:\n\n```bash\nMessages\n========\n\napp.py\n  Line: 12\n    pydocstyle: D212 / Multi-line docstring summary should start at the first line\n    pydocstyle: D407 / Missing dashed underline after section ('Parameters')\n    pydocstyle: D406 / Section name should end with a newline ('Parameters', not 'Parameters:')\n    pydocstyle: D417 / Missing argument descriptions in the docstring (argument(s) config_class are missing descriptions in 'create_app' docstring)\n    pydocstyle: D413 / Missing blank line after last section ('Returns')\n    pydocstyle: D407 / Missing dashed underline after section ('Returns')\n    pydocstyle: D406 / Section name should end with a newline ('Returns', not 'Returns:')\n```\n\n\u003c/details\u003e\n\nRun bandit security tests:\n\n```bash\nbandit -r . -ll -x ./venv\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003ebandit output\u003c/summary\u003e\n\n```bash\n[main]  INFO    profile include tests: None\n[main]  INFO    profile exclude tests: None\n[main]  INFO    cli include tests: None\n[main]  INFO    cli exclude tests: None\n[main]  INFO    running on Python 3.8.18\nRun started:2023-11-18 17:10:55.851705\n\nTest results:\n        No issues identified.\n\nCode scanned:\n        Total lines of code: 430\n        Total lines skipped (#nosec): 0\n\nRun metrics:\n        Total issues (by severity):\n                Undefined: 0\n                Low: 0\n                Medium: 0\n                High: 0\n        Total issues (by confidence):\n                Undefined: 0\n                Low: 0\n                Medium: 0\n                High: 0\nFiles skipped (0):\n```\n\n\u003c/details\u003e\n\n\n### Unit Tests\n\nRun unit tests:\n\n```bash\npython3 -m unittest discover -s tests --verbose\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eunittest output\u003c/summary\u003e\n\n```bash\ntest_product_model (test_product_service.ProductModelTestCase)\nTest the behavior of the Product model. ... ok\ntest_add_product_service (test_product_service.ProductServiceLayerTestCase)\nTest the 'add_product' method of the ProductService class. ... ok\ntest_delete_product (test_product_service.ProductServiceTestCase)\nTest the deletion of a product via the API. ... 2023-11-18 19:12:26,697 - views.product_views - INFO - creating a new product\n2023-11-18 19:12:26,710 - views.product_views - INFO - product was deleted: product_id=1\nok\ntest_product_creation (test_product_service.ProductServiceTestCase)\nTest the creation of a product via the API. ... 2023-11-18 19:12:26,720 - views.product_views - INFO - creating a new product\nok\ntest_product_retrieval (test_product_service.ProductServiceTestCase)\nTest the retrieval of a product via the API. ... 2023-11-18 19:12:26,740 - views.product_views - INFO - retrieving details for product id=1\nok\ntest_update_product (test_product_service.ProductServiceTestCase)\nTest the updating of a product via the API. ... 2023-11-18 19:12:26,750 - views.product_views - INFO - creating a new product\n2023-11-18 19:12:26,759 - views.product_views - INFO - updating product details for product id=1\nok\n\n----------------------------------------------------------------------\nRan 6 tests in 0.121s\n\nOK\n```\n\n\u003c/details\u003e\n\n\n### Code Coverage\n\nRun code coverage:\n\n```bash\ncoverage run --rcfile .coveragerc -m unittest discover -s tests \n```\n\n\u003cdetails\u003e\n  \u003csummary\u003ecoverage output\u003c/summary\u003e\n\n```bash\n..2023-11-18 19:13:21,309 - views.product_views - INFO - creating a new product\n2023-11-18 19:13:21,327 - views.product_views - INFO - product was deleted: product_id=1\n.2023-11-18 19:13:21,344 - views.product_views - INFO - creating a new product\n.2023-11-18 19:13:21,371 - views.product_views - INFO - retrieving details for product id=1\n.2023-11-18 19:13:21,385 - views.product_views - INFO - creating a new product\n2023-11-18 19:13:21,398 - views.product_views - INFO - updating product details for product id=1\n.\n----------------------------------------------------------------------\nRan 6 tests in 0.161s\n\nOK\n```\n\n\u003c/details\u003e\n\nRun the coverage report:\n\n```bash\ncoverage report --rcfile .coveragerc\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003ecoverage report output\u003c/summary\u003e\n\n```bash\nName                            Stmts   Miss  Cover\n---------------------------------------------------\napp.py                             17      0   100%\nconfig.py                           8      0   100%\ndatabase/db.py                      5      2    60%\nmodels/product.py                  11      1    91%\nservices/product_service.py        35      3    91%\nshared/logging_utils.py            19      1    95%\ntests/test_product_service.py      85      1    99%\nviews/product_views.py             40      8    80%\n---------------------------------------------------\nTOTAL                             220     16    93%\n```\n\n\u003c/details\u003e\n\n### Running tests with docker\n\nTests can be run as instructed above or can be run all at once using docker:\n\n```bash\nmake test-docker\n```\n\n## Health Checks\n\nThe application has two endpoints for healthchecks:\n\n- `/probes/health` : when application is ready\n- `/probes/ready`  : when database is ready\n\n## Database Migrations\n\nThis application uses the [Flask-Migrate](https://flask-migrate.readthedocs.io/en/latest/) extension for a [Alembic](https://alembic.sqlalchemy.org/en/latest/) migration environment.\n\n\u003cdetails\u003e\n  \u003csummary\u003eTo run database migrations:\u003c/summary\u003e\n\nSetting the environment:\n\n```bash\nexport FLASK_APP=app:create_app\nexport FLASK_ENV=development\n```\n\nRunning the database migrations\n\n```bash\nflask db init\nflask db migrate -m \"Initial db migration\"\nflask db upgrade\n```\n\nThe database versioning directory structure:\n\n```bash\n├── migrations\n│   ├── README\n│   ├── alembic.ini\n│   ├── env.py\n│   ├── script.py.mako\n│   └── versions\n├── models\n│   └── product.py\n```\n\n\u003c/details\u003e\n\nResources:\n- [Database Migrations with Flask](https://code.likeagirl.io/database-migrations-in-python-with-flask-with-alembic-442d11eb14d3)\n\n## Swagger API Documentation\n\nSwagger has been implemented with [Flasggr](https://github.com/flasgger/flasgger)\n\nSwagger Documentation is available at:\n- http://localhost:5000/apidocs/","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Fflask-api-structured-primer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruanbekker%2Fflask-api-structured-primer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Fflask-api-structured-primer/lists"}