├── .coveragerc ├── .flake8 ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── actions │ └── setup-env │ │ └── action.yml └── workflows │ ├── docs-deploy.yml │ ├── master-deploy.yml │ ├── master.yml │ ├── test-deploy.yml │ └── test.yml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CITATION.cff ├── LICENSE ├── README.md ├── benchmarks ├── README.md ├── aiohttp │ ├── AIOHTTP.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py ├── benchmark.py ├── falcon │ ├── FALCON.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py ├── flask │ ├── FLASK.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py ├── quart │ ├── QUART.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py ├── sanic │ ├── SANIC.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py ├── starlette │ ├── STARLETTE.md │ ├── with_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py │ └── without_fastopenapi │ │ ├── run.py │ │ ├── schemas.py │ │ └── storage.py └── tornado │ ├── TORNADO.md │ ├── with_fastopenapi │ ├── run.py │ ├── schemas.py │ └── storage.py │ └── without_fastopenapi │ ├── run.py │ ├── schemas.py │ └── storage.py ├── docs ├── de │ ├── docs │ │ ├── advanced │ │ │ ├── advanced_usage.md │ │ │ └── api_reference.md │ │ ├── changelog.md │ │ ├── contributing.md │ │ ├── faq.md │ │ ├── frameworks │ │ │ ├── aiohttp.md │ │ │ ├── falcon.md │ │ │ ├── flask.md │ │ │ ├── quart.md │ │ │ ├── sanic.md │ │ │ ├── starlette.md │ │ │ └── tornado.md │ │ ├── getting_started │ │ │ ├── installation.md │ │ │ ├── quickstart.md │ │ │ └── usage.md │ │ ├── img │ │ │ ├── HelloRedoc.jpg │ │ │ ├── HelloSwagger.jpg │ │ │ └── favicon.png │ │ └── index.md │ └── mkdocs.yml ├── en │ ├── docs │ │ ├── advanced │ │ │ ├── advanced_usage.md │ │ │ └── api_reference.md │ │ ├── changelog.md │ │ ├── contributing.md │ │ ├── faq.md │ │ ├── frameworks │ │ │ ├── aiohttp.md │ │ │ ├── falcon.md │ │ │ ├── flask.md │ │ │ ├── quart.md │ │ │ ├── sanic.md │ │ │ ├── starlette.md │ │ │ └── tornado.md │ │ ├── getting_started │ │ │ ├── installation.md │ │ │ ├── quickstart.md │ │ │ └── usage.md │ │ ├── img │ │ │ ├── HelloRedoc.jpg │ │ │ ├── HelloSwagger.jpg │ │ │ └── favicon.png │ │ └── index.md │ └── mkdocs.yml ├── es │ ├── docs │ │ ├── advanced │ │ │ ├── advanced_usage.md │ │ │ └── api_reference.md │ │ ├── changelog.md │ │ ├── contributing.md │ │ ├── faq.md │ │ ├── frameworks │ │ │ ├── aiohttp.md │ │ │ ├── falcon.md │ │ │ ├── flask.md │ │ │ ├── quart.md │ │ │ ├── sanic.md │ │ │ ├── starlette.md │ │ │ └── tornado.md │ │ ├── getting_started │ │ │ ├── installation.md │ │ │ ├── quickstart.md │ │ │ └── usage.md │ │ ├── img │ │ │ ├── HelloRedoc.jpg │ │ │ ├── HelloSwagger.jpg │ │ │ └── favicon.png │ │ └── index.md │ └── mkdocs.yml ├── fr │ ├── docs │ │ ├── advanced │ │ │ ├── advanced_usage.md │ │ │ └── api_reference.md │ │ ├── changelog.md │ │ ├── contributing.md │ │ ├── faq.md │ │ ├── frameworks │ │ │ ├── aiohttp.md │ │ │ ├── falcon.md │ │ │ ├── flask.md │ │ │ ├── quart.md │ │ │ ├── sanic.md │ │ │ ├── starlette.md │ │ │ └── tornado.md │ │ ├── getting_started │ │ │ ├── installation.md │ │ │ ├── quickstart.md │ │ │ └── usage.md │ │ ├── img │ │ │ ├── HelloRedoc.jpg │ │ │ ├── HelloSwagger.jpg │ │ │ └── favicon.png │ │ └── index.md │ └── mkdocs.yml └── ru │ ├── docs │ ├── advanced │ │ ├── advanced_usage.md │ │ └── api_reference.md │ ├── changelog.md │ ├── contributing.md │ ├── faq.md │ ├── frameworks │ │ ├── aiohttp.md │ │ ├── falcon.md │ │ ├── flask.md │ │ ├── quart.md │ │ ├── sanic.md │ │ ├── starlette.md │ │ └── tornado.md │ ├── getting_started │ │ ├── installation.md │ │ ├── quickstart.md │ │ └── usage.md │ ├── img │ │ ├── HelloRedoc.jpg │ │ ├── HelloSwagger.jpg │ │ └── favicon.png │ └── index.md │ └── mkdocs.yml ├── examples ├── aiohttp │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── aiohttp_example.py ├── falcon │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── falcon_example.py ├── flask │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── flask_example.py ├── quart │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── quart_example.py ├── sanic │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── sanic_example.py ├── starlette │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py ├── starlette_example.py ├── tornado │ ├── app │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── routes.py │ │ │ └── v1 │ │ │ │ ├── __init__.py │ │ │ │ ├── authors.py │ │ │ │ └── posts.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── authors.py │ │ │ └── posts.py │ │ └── storage.py │ └── run.py └── tornado_example.py ├── fastopenapi ├── __init__.py ├── base_router.py ├── error_handler.py └── routers │ ├── __init__.py │ ├── aiohttp.py │ ├── falcon.py │ ├── flask.py │ ├── quart.py │ ├── sanic.py │ ├── starlette.py │ └── tornado.py ├── logo.png ├── pyproject.toml ├── pytest.ini └── tests ├── __init__.py ├── aiohttp ├── __init__.py ├── conftest.py ├── test_aiohttp_integration.py └── test_aiohttp_router.py ├── falcon ├── __init__.py ├── conftest.py ├── test_falcon_integration.py └── test_falcon_router.py ├── flask ├── __init__.py ├── conftest.py ├── test_flask_integration.py └── test_flask_router.py ├── quart ├── __init__.py ├── conftest.py ├── test_quart_integration.py └── test_quart_router.py ├── sanic ├── __init__.py ├── conftest.py ├── test_sanic_integration.py └── test_sanic_router.py ├── starlette ├── __init__.py ├── conftest.py ├── test_starlette_integration.py └── test_starlette_router.py ├── test_base_router.py ├── test_routers_import.py └── tornado ├── __init__.py ├── test_dynamic_handler.py ├── test_tornado_integration.py └── test_tornado_router.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = fastopenapi 3 | omit = 4 | */site-packages/* 5 | branch = True 6 | 7 | [report] 8 | show_missing = True 9 | skip_covered = True 10 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude= 3 | .git 4 | 5 | max-complexity=10 6 | max-line-length=88 7 | 8 | inline-quotes = " 9 | import-order-style=pep8 -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | FastOpenAPI is built with one goal: to make life easier for developers working with different web frameworks. 4 | It's an open tool for open people. 5 | 6 | ## What We Expect 7 | 8 | - Be respectful and constructive. We're all here to make things better. 9 | - Help others if you can. Ask for help if you need it. 10 | - Feedback is welcome — especially if it makes the tool easier to use. 11 | 12 | ## What We Don’t Accept 13 | 14 | - Disrespectful or aggressive behavior 15 | - Personal attacks or gatekeeping 16 | - Any form of harassment 17 | 18 | ## Need to Talk? 19 | 20 | If you run into a problem with another contributor or feel something’s off, you can open a **GitHub security advisory** or reach out privately via the repo contacts. We’ll handle it respectfully and discreetly. 21 | 22 | --- 23 | 24 | Let’s keep FastOpenAPI useful, respectful, and open for everyone who wants to improve it. 25 | 26 | --- 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report to help us improve FastOpenAPI 3 | title: "[Bug] " 4 | labels: [bug] 5 | assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to report a bug! Please fill out this form so we can better understand the issue. 12 | 13 | - type: input 14 | id: environment 15 | attributes: 16 | label: Environment 17 | description: Describe your environment (Python version, OS, FastOpenAPI version, framework used, etc.) 18 | placeholder: Python 3.11, FastOpenAPI 0.7.0, Ubuntu 22.04, Flask 2.2 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: description 24 | attributes: 25 | label: Bug Description 26 | description: What happened and what did you expect to happen? 27 | placeholder: A clear and concise description of the problem. 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | id: steps 33 | attributes: 34 | label: Steps to Reproduce 35 | description: How can we reproduce the bug? 36 | placeholder: | 37 | 1. Go to '...' 38 | 2. Use this code snippet '...' 39 | 3. See the error message '...' 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | id: logs 45 | attributes: 46 | label: Logs or Tracebacks 47 | description: Paste relevant logs or tracebacks here. 48 | render: shell 49 | 50 | - type: input 51 | id: contact 52 | attributes: 53 | label: Contact 54 | description: Optional – how can we reach you if we need more info? 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest a new feature or enhancement 3 | title: "[Feature] " 4 | labels: [enhancement] 5 | assignees: [] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Have an idea for FastOpenAPI? We'd love to hear it! 12 | 13 | - type: input 14 | id: motivation 15 | attributes: 16 | label: What problem are you trying to solve? 17 | description: Explain the use case or need behind this feature. 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: proposal 23 | attributes: 24 | label: Proposed Solution 25 | description: Describe the feature you’d like to see and how it might work. 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: alternatives 31 | attributes: 32 | label: Alternatives Considered 33 | description: Have you tried any alternative approaches or workarounds? 34 | 35 | - type: input 36 | id: contact 37 | attributes: 38 | label: Contact 39 | description: Optional – how can we reach you if we need more info? 40 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request 2 | 3 | Thanks for contributing to FastOpenAPI! 4 | Before submitting, please make sure you've read [CONTRIBUTING.md](../CONTRIBUTING.md). 5 | 6 | --- 7 | 8 | ## What’s Included? 9 | 10 | - [ ] New feature 11 | - [ ] Bug fix 12 | - [ ] Documentation improvement 13 | - [ ] Refactor / internal change 14 | 15 | **Describe your changes:** 16 | 17 | > Concise and clear: what did you change, why, and which parts/frameworks are affected? 18 | 19 | --- 20 | 21 | ## How Was It Tested? 22 | 23 | > Describe how you tested the changes. 24 | > Were new tests added? Are examples working? 25 | 26 | --- 27 | 28 | ## Anything Else? 29 | 30 | > Optional: 31 | > - Any TODOs or follow-up items? 32 | > - Known limitations or trade-offs? 33 | > - Anything you'd like reviewers to focus on? 34 | 35 | --- 36 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you discover a security vulnerability in **FastOpenAPI**, we ask that you report it responsibly using GitHub's private security advisories. 6 | 7 | 👉 [Report a vulnerability](https://github.com/mr-fatalyst/fastopenapi/security/advisories/new) 8 | 9 | Please include the following details: 10 | - Description of the vulnerability 11 | - Steps to reproduce 12 | - Potential impact 13 | - Suggested fix (if any) 14 | 15 | I appreciate all reports and aim to respond within **5 business days**. 16 | 17 | --- 18 | 19 | ## ⚠️ Project Status 20 | 21 | FastOpenAPI is currently under **active development**. 22 | Please be aware that the project may contain bugs or unstable features. 23 | 24 | I am grateful for any reports of security issues, unexpected behaviors, or improvement suggestions — your feedback helps make the project better for everyone 🙏 25 | 26 | --- 27 | 28 | ## Responsible Disclosure 29 | 30 | Please do not create public issues for security problems. Use the GitHub advisory form linked above to report them privately. 31 | 32 | Thank you for helping to keep FastOpenAPI secure! 33 | 34 | --- 35 | -------------------------------------------------------------------------------- /.github/actions/setup-env/action.yml: -------------------------------------------------------------------------------- 1 | name: "Setup Environment" 2 | description: "Composite action for setting up Python environment with caching, Poetry installation and dependency installation using --all-extras flag." 3 | 4 | inputs: 5 | python-version: 6 | description: "Python version to use." 7 | required: false 8 | default: "3.10" 9 | install-flags: 10 | description: "Flags for poetry install command." 11 | required: false 12 | default: "--all-extras" 13 | 14 | runs: 15 | using: "composite" 16 | steps: 17 | - name: Setup Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: ${{ inputs.python-version }} 21 | 22 | - name: Cache pip dependencies 23 | uses: actions/cache@v3 24 | with: 25 | path: ~/.cache/pip 26 | key: ${{ runner.os }}-pip-${{ hashFiles('**/poetry.lock') }} 27 | restore-keys: | 28 | ${{ runner.os }}-pip- 29 | 30 | - name: Cache Poetry dependencies 31 | uses: actions/cache@v3 32 | with: 33 | path: ~/.cache/pypoetry 34 | key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} 35 | restore-keys: | 36 | ${{ runner.os }}-poetry- 37 | 38 | - name: Install Poetry 39 | run: | 40 | curl -sSL https://install.python-poetry.org | python3 - 41 | echo "$HOME/.local/bin" >> $GITHUB_PATH 42 | shell: bash 43 | 44 | - name: Install dependencies 45 | run: poetry install ${{ inputs.install-flags }} 46 | shell: bash 47 | -------------------------------------------------------------------------------- /.github/workflows/docs-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'docs/**' 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - uses: actions/setup-python@v5 20 | with: 21 | python-version: '3.x' 22 | 23 | - name: Install MkDocs 24 | run: pip install mkdocs-material 25 | 26 | - name: Build Documentation 27 | run: | 28 | mkdocs build -f docs/en/mkdocs.yml -d $(pwd)/site 29 | mkdocs build -f docs/ru/mkdocs.yml -d $(pwd)/site/ru 30 | mkdocs build -f docs/es/mkdocs.yml -d $(pwd)/site/es 31 | mkdocs build -f docs/de/mkdocs.yml -d $(pwd)/site/de 32 | mkdocs build -f docs/fr/mkdocs.yml -d $(pwd)/site/fr 33 | 34 | - name: Ensure site exists 35 | run: mkdir -p site 36 | 37 | - name: Write CNAME 38 | run: echo 'fastopenapi.fatalyst.dev' > site/CNAME 39 | 40 | - name: Deploy to GitHub Pages 41 | uses: peaceiris/actions-gh-pages@v4 42 | with: 43 | github_token: ${{ secrets.GITHUB_TOKEN }} 44 | publish_dir: ./site 45 | -------------------------------------------------------------------------------- /.github/workflows/master-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Master Deploy 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | coverage: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Setup environment (coverage) 17 | uses: ./.github/actions/setup-env 18 | with: 19 | python-version: "3.10" 20 | install-flags: "--all-extras" 21 | 22 | - name: Run tests with coverage 23 | run: | 24 | poetry run coverage run -m pytest 25 | poetry run coverage report 26 | poetry run coverage xml 27 | 28 | - name: Upload coverage to Codecov 29 | env: 30 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 31 | run: bash <(curl -s https://codecov.io/bash) -t $CODECOV_TOKEN -f coverage.xml 32 | 33 | deploy-prod: 34 | needs: coverage 35 | runs-on: ubuntu-latest 36 | env: 37 | POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} 38 | steps: 39 | - uses: actions/checkout@v3 40 | with: 41 | fetch-depth: 0 42 | 43 | - name: Setup environment (deploy) 44 | uses: ./.github/actions/setup-env 45 | with: 46 | python-version: "3.10" 47 | install-flags: "--all-extras" 48 | 49 | - name: Configure PyPI 50 | run: | 51 | poetry config repositories.pypi https://upload.pypi.org/legacy/ 52 | poetry config pypi-token.pypi $POETRY_PYPI_TOKEN_PYPI 53 | 54 | - name: Publish to PyPI 55 | run: | 56 | poetry build 57 | poetry publish 58 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - 'benchmarks/**' 9 | - 'docs/**' 10 | - 'examples/**' 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Setup environment (lint) 21 | uses: ./.github/actions/setup-env 22 | with: 23 | python-version: "3.10" 24 | install-flags: "--all-extras" 25 | 26 | - name: Run linters 27 | run: poetry run pre-commit run --all-files 28 | 29 | test: 30 | needs: lint 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | with: 35 | fetch-depth: 0 36 | 37 | - name: Setup environment (test) 38 | uses: ./.github/actions/setup-env 39 | with: 40 | python-version: "3.10" 41 | install-flags: "--all-extras" 42 | 43 | - name: Run tests 44 | run: poetry run pytest 45 | -------------------------------------------------------------------------------- /.github/workflows/test-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Test Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - test 7 | 8 | jobs: 9 | deploy-test: 10 | runs-on: ubuntu-latest 11 | env: 12 | POETRY_PYPI_TOKEN_TEST_PYPI: ${{ secrets.TEST_PYPI_API_TOKEN }} 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Setup environment (deploy) 20 | uses: ./.github/actions/setup-env 21 | with: 22 | python-version: "3.10" 23 | install-flags: "--all-extras" 24 | 25 | - name: Configure Test PyPI 26 | run: | 27 | poetry config repositories.test-pypi https://test.pypi.org/legacy/ 28 | poetry config pypi-token.test-pypi $POETRY_PYPI_TOKEN_TEST_PYPI 29 | 30 | - name: Publish to Test PyPI 31 | run: | 32 | poetry build 33 | poetry publish -r test-pypi 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - test 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Setup environment (lint) 18 | uses: ./.github/actions/setup-env 19 | with: 20 | python-version: "3.10" 21 | install-flags: "--all-extras" 22 | 23 | - name: Run linters 24 | run: poetry run pre-commit run --all-files 25 | 26 | test: 27 | needs: lint 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v3 32 | with: 33 | fetch-depth: 0 34 | 35 | - name: Setup environment (test) 36 | uses: ./.github/actions/setup-env 37 | with: 38 | python-version: "3.10" 39 | install-flags: "--all-extras" 40 | 41 | - name: Run tests 42 | run: poetry run pytest 43 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | multi_line_output = 3 3 | include_trailing_comma = True 4 | force_grid_wrap = 0 5 | use_parentheses = True 6 | ensure_newline_before_comments = True 7 | line_length = 88 -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/asottile/pyupgrade 3 | rev: v3.19.1 4 | hooks: 5 | - id: pyupgrade 6 | args: [ --py310-plus ] 7 | 8 | - repo: https://github.com/pycqa/autoflake 9 | rev: v2.3.1 10 | hooks: 11 | - id: autoflake 12 | args: [ '--in-place', '--remove-all-unused-imports', '--remove-unused-variable','--exclude=__init__.py' ] 13 | 14 | - repo: https://github.com/pycqa/isort 15 | rev: 6.0.1 16 | hooks: 17 | - id: isort 18 | args: ["--profile", "black", "--filter-files"] 19 | 20 | - repo: https://github.com/psf/black 21 | rev: 25.1.0 22 | hooks: 23 | - id: black 24 | args: [--target-version, py310, --line-length=88] 25 | language_version: python3.10 26 | 27 | - repo: https://github.com/pycqa/flake8 28 | rev: 7.1.2 29 | hooks: 30 | - id: flake8 31 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: FastOpenAPI 3 | message: 'If you use this software, please cite it as below.' 4 | type: software 5 | authors: 6 | - given-names: Nikita 7 | family-names: Ryzhenkov 8 | email: nikita.ryzhenkoff@gmail.com 9 | identifiers: 10 | repository-code: 'https://github.com/mr-fatalyst/fastopenapi' 11 | url: 'https://fastopenapi.fatalyst.dev/' 12 | abstract: >- 13 | FastOpenAPI is a library for generating and integrating OpenAPI schemas 14 | using Pydantic v2 and various frameworks 15 | (AioHttp, Falcon, Flask, Quart, Sanic, Starlette, Tornado). 16 | keywords: 17 | - aiohttp 18 | - falcon 19 | - flask 20 | - quart 21 | - sanic 22 | - starlette 23 | - tornado 24 | - pydantic 25 | - openapi 26 | license: MIT 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Nikita Ryzhenkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmarks/aiohttp/AIOHTTP.md: -------------------------------------------------------------------------------- 1 | # AioHttp Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 8.5742 sec total, 0.86 ms per request 10 | GET one record: 8.7001 sec total, 0.87 ms per request 11 | POST new record: 9.0483 sec total, 0.90 ms per request 12 | PUT record: 9.1385 sec total, 0.91 ms per request 13 | PATCH record: 9.1465 sec total, 0.91 ms per request 14 | DELETE record: 17.7022 sec total, 1.77 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 8.7114 sec total, 0.87 ms per request 24 | GET one record: 9.2981 sec total, 0.93 ms per request 25 | POST new record: 9.4486 sec total, 0.94 ms per request 26 | PUT record: 9.4418 sec total, 0.94 ms per request 27 | PATCH record: 9.4253 sec total, 0.94 ms per request 28 | DELETE record: 18.2327 sec total, 1.82 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |------------------|----------|-------------|-----------------| 37 | | GET all records | 0.86 ms | 0.87 ms | +0.01 ms (+1.6%) | 38 | | GET one record | 0.87 ms | 0.93 ms | +0.06 ms (+6.9%) | 39 | | POST new record | 0.90 ms | 0.94 ms | +0.04 ms (+4.4%) | 40 | | PUT record | 0.91 ms | 0.94 ms | +0.03 ms (+3.3%) | 41 | | PATCH record | 0.91 ms | 0.94 ms | +0.03 ms (+3.0%) | 42 | | DELETE record | 1.77 ms | 1.82 ms | +0.05 ms (+3.0%) | 43 | 44 | 45 | 46 | --- 47 | 48 | [<< Back](../README.md) 49 | 50 | --- -------------------------------------------------------------------------------- /benchmarks/aiohttp/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/aiohttp/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/aiohttp/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/aiohttp/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/falcon/FALCON.md: -------------------------------------------------------------------------------- 1 | # Falcon Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 8.6085 sec total, 0.86 ms per request 10 | GET one record: 8.7683 sec total, 0.88 ms per request 11 | POST new record: 9.1847 sec total, 0.92 ms per request 12 | PUT record: 9.3219 sec total, 0.93 ms per request 13 | PATCH record: 9.1856 sec total, 0.92 ms per request 14 | DELETE record: 17.7793 sec total, 1.78 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 8.8547 sec total, 0.89 ms per request 24 | GET one record: 9.0380 sec total, 0.90 ms per request 25 | POST new record: 9.5613 sec total, 0.96 ms per request 26 | PUT record: 9.5503 sec total, 0.96 ms per request 27 | PATCH record: 9.4769 sec total, 0.95 ms per request 28 | DELETE record: 18.6130 sec total, 1.86 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |-------------------------|----------|-------------|----------------| 37 | | GET all records | 0.86 ms | 0.89 ms | 0.02 ms (+2.9%) | 38 | | GET one record | 0.88 ms | 0.90 ms | 0.03 ms (+3.1%) | 39 | | POST new record | 0.92 ms | 0.96 ms | 0.04 ms (+4.1%) | 40 | | PUT record | 0.93 ms | 0.96 ms | 0.02 ms (+2.5%) | 41 | | PATCH record | 0.92 ms | 0.95 ms | 0.03 ms (+3.2%) | 42 | | DELETE record | 1.78 ms | 1.86 ms | 0.08 ms (+4.7%) | 43 | 44 | --- 45 | 46 | [<< Back](../README.md) 47 | 48 | --- -------------------------------------------------------------------------------- /benchmarks/falcon/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/falcon/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/falcon/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/falcon/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/flask/FLASK.md: -------------------------------------------------------------------------------- 1 | # Flask Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 12.3051 sec total, 1.23 ms per request 10 | GET one record: 12.5205 sec total, 1.25 ms per request 11 | POST new record: 13.2638 sec total, 1.33 ms per request 12 | PUT record: 13.3373 sec total, 1.33 ms per request 13 | PATCH record: 13.1245 sec total, 1.31 ms per request 14 | DELETE record: 25.7178 sec total, 2.57 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 12.7424 sec total, 1.27 ms per request 24 | GET one record: 13.0017 sec total, 1.30 ms per request 25 | POST new record: 13.5450 sec total, 1.35 ms per request 26 | PUT record: 13.5281 sec total, 1.35 ms per request 27 | PATCH record: 13.5548 sec total, 1.36 ms per request 28 | DELETE record: 26.7906 sec total, 2.68 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |-------------------------|----------|-------------|----------------| 37 | | GET all records | 1.23 ms | 1.27 ms | 0.04 ms (+3.6%) | 38 | | GET one record | 1.25 ms | 1.30 ms | 0.05 ms (+3.8%) | 39 | | POST new record | 1.33 ms | 1.35 ms | 0.03 ms (+2.1%) | 40 | | PUT record | 1.33 ms | 1.35 ms | 0.02 ms (+1.4%) | 41 | | PATCH record | 1.31 ms | 1.36 ms | 0.04 ms (+3.3%) | 42 | | DELETE record | 2.57 ms | 2.68 ms | 0.11 ms (+4.2%) | 43 | 44 | --- 45 | 46 | [<< Back](../README.md) 47 | 48 | --- -------------------------------------------------------------------------------- /benchmarks/flask/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/flask/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/flask/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/flask/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/quart/QUART.md: -------------------------------------------------------------------------------- 1 | # Quart Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 16.3760 sec total, 1.64 ms per request 10 | GET one record: 17.7782 sec total, 1.78 ms per request 11 | POST new record: 19.8376 sec total, 1.98 ms per request 12 | PUT record: 20.4346 sec total, 2.04 ms per request 13 | PATCH record: 19.7331 sec total, 1.97 ms per request 14 | DELETE record: 37.4556 sec total, 3.75 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 17.4752 sec total, 1.75 ms per request 24 | GET one record: 18.3059 sec total, 1.83 ms per request 25 | POST new record: 19.9647 sec total, 2.00 ms per request 26 | PUT record: 19.3761 sec total, 1.94 ms per request 27 | PATCH record: 19.5880 sec total, 1.96 ms per request 28 | DELETE record: 40.6837 sec total, 4.07 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |-------------------------|----------|-------------|----------------| 37 | | GET all records | 1.64 ms | 1.75 ms | 0.11 ms (+6.7%) | 38 | | GET one record | 1.78 ms | 1.83 ms | 0.05 ms (+3.0%) | 39 | | POST new record | 1.98 ms | 2.00 ms | 0.01 ms (+0.6%) | 40 | | PUT record | 2.04 ms | 1.94 ms | -0.11 ms (-5.2%) | 41 | | PATCH record | 1.97 ms | 1.96 ms | -0.01 ms (-0.7%) | 42 | | DELETE record | 3.75 ms | 4.07 ms | 0.32 ms (+8.6%) | 43 | 44 | --- 45 | 46 | [<< Back](../README.md) 47 | 48 | --- -------------------------------------------------------------------------------- /benchmarks/quart/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/quart/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/quart/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/quart/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/sanic/SANIC.md: -------------------------------------------------------------------------------- 1 | # Sanic Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 8.7815 sec total, 0.88 ms per request 10 | GET one record: 9.0136 sec total, 0.90 ms per request 11 | POST new record: 9.4816 sec total, 0.95 ms per request 12 | PUT record: 9.2173 sec total, 0.92 ms per request 13 | PATCH record: 9.2911 sec total, 0.93 ms per request 14 | DELETE record: 18.5361 sec total, 1.85 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 9.0011 sec total, 0.90 ms per request 24 | GET one record: 9.3199 sec total, 0.93 ms per request 25 | POST new record: 9.6575 sec total, 0.97 ms per request 26 | PUT record: 9.6792 sec total, 0.97 ms per request 27 | PATCH record: 9.8153 sec total, 0.98 ms per request 28 | DELETE record: 19.9205 sec total, 1.99 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |-------------------------|----------|-------------|----------------| 37 | | GET all records | 0.88 ms | 0.90 ms | 0.02 ms (+2.5%) | 38 | | GET one record | 0.90 ms | 0.93 ms | 0.03 ms (+3.4%) | 39 | | POST new record | 0.95 ms | 0.97 ms | 0.02 ms (+1.9%) | 40 | | PUT record | 0.92 ms | 0.97 ms | 0.05 ms (+5.0%) | 41 | | PATCH record | 0.93 ms | 0.98 ms | 0.05 ms (+5.6%) | 42 | | DELETE record | 1.85 ms | 1.99 ms | 0.14 ms (+7.5%) | 43 | 44 | --- 45 | 46 | [<< Back](../README.md) 47 | 48 | --- -------------------------------------------------------------------------------- /benchmarks/sanic/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/sanic/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/sanic/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/sanic/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/starlette/STARLETTE.md: -------------------------------------------------------------------------------- 1 | # Starlette Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 8.6688 sec total, 0.87 ms per request 10 | GET one record: 8.9452 sec total, 0.89 ms per request 11 | POST new record: 9.2628 sec total, 0.93 ms per request 12 | PUT record: 9.2470 sec total, 0.92 ms per request 13 | PATCH record: 9.3018 sec total, 0.93 ms per request 14 | DELETE record: 20.9903 sec total, 2.10 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 9.2649 sec total, 0.93 ms per request 24 | GET one record: 9.2726 sec total, 0.93 ms per request 25 | POST new record: 9.7103 sec total, 0.97 ms per request 26 | PUT record: 9.7489 sec total, 0.97 ms per request 27 | PATCH record: 9.6531 sec total, 0.97 ms per request 28 | DELETE record: 21.9329 sec total, 2.19 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |------------------------|----------|-------------|----------------| 37 | | GET all records | 0.87 ms | 0.93 ms | 0.06 ms (+6.9%) | 38 | | GET one record | 0.89 ms | 0.93 ms | 0.03 ms (+3.7%) | 39 | | POST new record | 0.93 ms | 0.97 ms | 0.04 ms (+4.8%) | 40 | | PUT record | 0.92 ms | 0.97 ms | 0.05 ms (+5.4%) | 41 | | PATCH record | 0.93 ms | 0.97 ms | 0.04 ms (+3.8%) | 42 | | DELETE record | 2.10 ms | 2.19 ms | 0.09 ms (+4.5%) | 43 | 44 | 45 | --- 46 | 47 | [<< Back](../README.md) 48 | 49 | --- -------------------------------------------------------------------------------- /benchmarks/starlette/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/starlette/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/starlette/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/starlette/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/tornado/TORNADO.md: -------------------------------------------------------------------------------- 1 | # Tornado Benchmark 2 | 3 | --- 4 | 5 | ## Testing Original Implementation 6 | ``` 7 | Original - Running 10000 iterations per endpoint 8 | -------------------------------------------------- 9 | GET all records: 9.3412 sec total, 0.93 ms per request 10 | GET one record: 9.5527 sec total, 0.96 ms per request 11 | POST new record: 9.9068 sec total, 0.99 ms per request 12 | PUT record: 9.8596 sec total, 0.99 ms per request 13 | PATCH record: 9.8612 sec total, 0.99 ms per request 14 | DELETE record: 19.0970 sec total, 1.91 ms per request 15 | ``` 16 | --- 17 | 18 | ## Testing FastOpenAPI Implementation 19 | 20 | ``` 21 | FastOpenAPI - Running 10000 iterations per endpoint 22 | -------------------------------------------------- 23 | GET all records: 9.5980 sec total, 0.96 ms per request 24 | GET one record: 9.9557 sec total, 1.00 ms per request 25 | POST new record: 10.2566 sec total, 1.03 ms per request 26 | PUT record: 10.4081 sec total, 1.04 ms per request 27 | PATCH record: 10.2608 sec total, 1.03 ms per request 28 | DELETE record: 19.9923 sec total, 2.00 ms per request 29 | ``` 30 | 31 | --- 32 | 33 | ## Performance Comparison (10000 iterations) 34 | 35 | | Endpoint | Original | FastOpenAPI | Difference | 36 | |--------------------------|----------|-------------|------------| 37 | | GET all records | 0.93 ms | 0.96 ms | 0.03 ms (+2.7%) | 38 | | GET one record | 0.96 ms | 1.00 ms | 0.04 ms (+4.2%) | 39 | | POST new record | 0.99 ms | 1.03 ms | 0.03 ms (+3.5%) | 40 | | PUT record | 0.99 ms | 1.04 ms | 0.05 ms (+5.6%) | 41 | | PATCH record | 0.99 ms | 1.03 ms | 0.04 ms (+4.1%) | 42 | | DELETE record | 1.91 ms | 2.00 ms | 0.09 ms (+4.7%) | 43 | 44 | --- 45 | 46 | [<< Back](../README.md) 47 | 48 | --- -------------------------------------------------------------------------------- /benchmarks/tornado/with_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | 10 | class RecordUpdate(BaseModel): 11 | title: str | None = Field(None, min_length=1, max_length=100) 12 | description: str | None = Field(None, max_length=500) 13 | is_completed: bool | None = None 14 | 15 | 16 | class RecordResponse(BaseModel): 17 | id: str 18 | title: str 19 | description: str | None = None 20 | is_completed: bool 21 | -------------------------------------------------------------------------------- /benchmarks/tornado/with_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /benchmarks/tornado/without_fastopenapi/schemas.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, field_validator 2 | 3 | 4 | class RecordCreate(BaseModel): 5 | title: str = Field(..., min_length=1, max_length=100) 6 | description: str | None = Field(None, max_length=500) 7 | is_completed: bool = Field(False) 8 | 9 | @field_validator("title") 10 | @classmethod 11 | def title_cannot_be_empty(cls, v: str) -> str: 12 | if not v.strip(): 13 | raise ValueError("Title cannot be empty") 14 | return v 15 | 16 | 17 | class RecordUpdate(BaseModel): 18 | title: str | None = Field(None, min_length=1, max_length=100) 19 | description: str | None = Field(None, max_length=500) 20 | is_completed: bool | None = None 21 | 22 | @field_validator("title") 23 | @classmethod 24 | def title_cannot_be_empty(cls, v: str | None) -> str | None: 25 | if v is not None and not v.strip(): 26 | raise ValueError("Title cannot be empty") 27 | return v 28 | 29 | 30 | class RecordResponse(BaseModel): 31 | id: str 32 | title: str 33 | description: str | None = None 34 | is_completed: bool 35 | -------------------------------------------------------------------------------- /benchmarks/tornado/without_fastopenapi/storage.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from .schemas import RecordCreate, RecordResponse, RecordUpdate 4 | 5 | 6 | class RecordStore: 7 | def __init__(self): 8 | self.records: dict[str, dict] = {} 9 | 10 | def create(self, record_data: RecordCreate) -> RecordResponse: 11 | record_id = str(uuid.uuid4()) 12 | record = {"id": record_id, **record_data.model_dump()} 13 | self.records[record_id] = record 14 | return RecordResponse(**record) 15 | 16 | def get_all(self) -> list[RecordResponse]: 17 | return [RecordResponse(**record) for record in self.records.values()] 18 | 19 | def get_by_id(self, record_id: str) -> RecordResponse | None: 20 | record = self.records.get(record_id) 21 | if record: 22 | return RecordResponse(**record) 23 | return None 24 | 25 | def update( 26 | self, record_id: str, record_data: RecordUpdate 27 | ) -> RecordResponse | None: 28 | if record_id not in self.records: 29 | return None 30 | 31 | update_data = record_data.model_dump(exclude_unset=True) 32 | if not update_data: 33 | return RecordResponse(**self.records[record_id]) 34 | 35 | for key, value in update_data.items(): 36 | if value is not None: 37 | self.records[record_id][key] = value 38 | 39 | return RecordResponse(**self.records[record_id]) 40 | 41 | def delete(self, record_id: str) -> bool: 42 | if record_id in self.records: 43 | del self.records[record_id] 44 | return True 45 | return False 46 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/aiohttp.md: -------------------------------------------------------------------------------- 1 | # AIOHTTP-Integration 2 | 3 | Dieses Kapitel zeigt, wie man FastOpenAPI mit **AIOHTTP** verwendet (ein asynchrones HTTP-Framework für Python). 4 | 5 | AIOHTTP-Anwendungen basieren auf `aiohttp.web.Application` und werden mit `aiohttp.web.run_app` ausgeführt. FastOpenAPI stellt den `AioHttpRouter` zur Integration bereit. 6 | 7 | ## Installation 8 | 9 | Stelle sicher, dass FastOpenAPI installiert ist: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder: 15 | 16 | ```bash 17 | pip install fastopenapi[aiohttp] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from aiohttp import web 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import AioHttpRouter 26 | 27 | app = web.Application() 28 | router = AioHttpRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Sag Hallo mit AIOHTTP""" 36 | return HelloResponse(message=f"Hallo, {name}! Hier ist aiohttp!") 37 | 38 | if __name__ == "__main__": 39 | web.run_app(app, host="127.0.0.1", port=8000) 40 | ``` 41 | 42 | ## Beispielprojekt 43 | 44 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/aiohttp/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/aiohttp) des Repositories. 45 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/falcon.md: -------------------------------------------------------------------------------- 1 | # Falcon-Integration 2 | 3 | In diesem Leitfaden wird die Verwendung von FastOpenAPI mit **Falcon** beschrieben, einem leistungsstarken Webframework. 4 | 5 | FastOpenAPI unterstützt Falcon über den `FalconRouter`, insbesondere bei Verwendung der ASGI-Schnittstelle für asynchrone Anwendungen. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder: 15 | 16 | ```bash 17 | pip install fastopenapi[falcon] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import falcon.asgi 24 | import uvicorn 25 | from pydantic import BaseModel 26 | from fastopenapi.routers import FalconRouter 27 | 28 | app = falcon.asgi.App() # ASGI-Anwendung (async-fähig) 29 | router = FalconRouter(app=app) # FastOpenAPI-Router einbinden 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Sag Hallo mit Falcon""" 37 | return HelloResponse(message=f"Hallo, {name}! Hier ist Falcon!") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Beispielprojekt 44 | 45 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/falcon/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/falcon) des Repositories. 46 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/flask.md: -------------------------------------------------------------------------------- 1 | # Flask-Integration 2 | 3 | Dieser Leitfaden zeigt die Integration von FastOpenAPI mit **Flask**, einem der beliebtesten Python-Webframeworks. 4 | 5 | Die Klasse `FlaskRouter` verbindet FastOpenAPI mit dem Routing-System von Flask. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder 15 | 16 | ```bash 17 | pip install fastopenapi[flask] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from flask import Flask 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import FlaskRouter 26 | 27 | app = Flask(__name__) 28 | router = FlaskRouter(app=app) # FastOpenAPI-Router einbinden 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | def hello(name: str): 35 | """Sag Hallo mit Flask""" 36 | return HelloResponse(message=f"Hallo, {name}! Hier ist Flask!") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Beispielprojekt 43 | 44 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/flask/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/flask) des Repositories. 45 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/quart.md: -------------------------------------------------------------------------------- 1 | # Quart-Integration 2 | 3 | Quart ist ein asynchrones Webframework mit einer API, die Flask sehr ähnlich ist (quasi ein Ersatz mit async-Unterstützung). 4 | 5 | FastOpenAPI bietet mit `QuartRouter` eine Integration speziell für Quart. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder 15 | 16 | ```bash 17 | pip install fastopenapi[quart] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from quart import Quart 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import QuartRouter 26 | 27 | app = Quart(__name__) 28 | router = QuartRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Sag Hallo mit Quart""" 36 | return HelloResponse(message=f"Hallo, {name}! Hier ist Quart!") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Beispielprojekt 43 | 44 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/quart/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/quart) des Repositories. 45 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/sanic.md: -------------------------------------------------------------------------------- 1 | # Sanic-Integration 2 | 3 | Sanic ist ein asynchrones Webframework, das für seine Geschwindigkeit bekannt ist. 4 | 5 | FastOpenAPI integriert sich in Sanic über den `SanicRouter`. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder 15 | 16 | ```bash 17 | pip install fastopenapi[sanic] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from sanic import Sanic 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import SanicRouter 26 | 27 | app = Sanic("MySanicApp") 28 | router = SanicRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Sag Hallo mit Sanic""" 36 | return HelloResponse(message=f"Hallo, {name}! Hier ist Sanic!") 37 | 38 | if __name__ == "__main__": 39 | app.run(host="0.0.0.0", port=8000) 40 | ``` 41 | 42 | ## Beispielprojekt 43 | 44 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/sanic/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/sanic) des Repositories. 45 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/starlette.md: -------------------------------------------------------------------------------- 1 | # Starlette-Integration 2 | 3 | Starlette ist ein leichtgewichtiges ASGI-Framework, auf dem auch FastAPI selbst basiert. 4 | 5 | FastOpenAPI bietet mit `StarletteRouter` eine direkte Integration in Starlette-Anwendungen. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder 15 | 16 | ```bash 17 | pip install fastopenapi[starlette] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import uvicorn 24 | from pydantic import BaseModel 25 | from starlette.applications import Starlette 26 | from fastopenapi.routers import StarletteRouter 27 | 28 | app = Starlette() 29 | router = StarletteRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Sag Hallo mit Starlette""" 37 | return HelloResponse(message=f"Hallo, {name}! Hier ist Starlette!") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Beispielprojekt 44 | 45 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/starlette/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/starlette) des Repositories. 46 | -------------------------------------------------------------------------------- /docs/de/docs/frameworks/tornado.md: -------------------------------------------------------------------------------- 1 | # Tornado-Integration 2 | 3 | Tornado ist ein etabliertes Webframework und eine asynchrone Netzwerkbibliothek für Python. 4 | 5 | FastOpenAPI unterstützt Tornado über den `TornadoRouter`, mit dem du OpenAPI-Dokumentation zu Tornado-Anwendungen hinzufügen kannst. 6 | 7 | ## Installation 8 | 9 | FastOpenAPI installieren: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | oder 15 | 16 | ```bash 17 | pip install fastopenapi[tornado] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import asyncio 24 | from pydantic import BaseModel 25 | from tornado.web import Application 26 | from fastopenapi.routers import TornadoRouter 27 | 28 | app = Application() 29 | router = TornadoRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | def hello(name: str): 36 | """Sag Hallo mit Tornado""" 37 | return HelloResponse(message=f"Hallo, {name}! Hier ist Tornado!") 38 | 39 | async def main(): 40 | app.listen(8000) 41 | await asyncio.Event().wait() 42 | 43 | if __name__ == "__main__": 44 | asyncio.run(main()) 45 | ``` 46 | 47 | ## Beispielprojekt 48 | 49 | Ein vollständiges Beispiel findest du im Verzeichnis [`examples/tornado/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/tornado) des Repositories. 50 | -------------------------------------------------------------------------------- /docs/de/docs/getting_started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | FastOpenAPI ist auf PyPI verfügbar und unterstützt **Python 3.10+**. Du kannst entweder nur die Kernbibliothek oder optionale Abhängigkeiten für bestimmte Webframeworks installieren. 4 | 5 | ## Voraussetzungen 6 | 7 | - **Python 3.10 oder höher** – erforderlich wegen moderner Typisierung und Unterstützung für Pydantic v2. 8 | - (Optional) Ein bestehendes Webframework (wie Flask, Starlette usw.), falls du eines integrieren möchtest. Falls noch nicht installiert, kannst du es über die entsprechenden Extras automatisch mitinstallieren. 9 | 10 | ## Installation via pip 11 | 12 | #### Nur FastOpenAPI installieren 13 | *Standardinstallation* 14 | ```bash 15 | pip install fastopenapi 16 | ``` 17 | 18 | #### FastOpenAPI mit spezifischem Framework installieren 19 | *Nützlich für neue Projekte ohne vorinstalliertes Framework* 20 | ```bash 21 | pip install fastopenapi[aiohttp] 22 | ``` 23 | ```bash 24 | pip install fastopenapi[falcon] 25 | ``` 26 | ```bash 27 | pip install fastopenapi[flask] 28 | ``` 29 | ```bash 30 | pip install fastopenapi[quart] 31 | ``` 32 | ```bash 33 | pip install fastopenapi[sanic] 34 | ``` 35 | ```bash 36 | pip install fastopenapi[starlette] 37 | ``` 38 | ```bash 39 | pip install fastopenapi[tornado] 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /docs/de/docs/img/HelloRedoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/de/docs/img/HelloRedoc.jpg -------------------------------------------------------------------------------- /docs/de/docs/img/HelloSwagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/de/docs/img/HelloSwagger.jpg -------------------------------------------------------------------------------- /docs/de/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/de/docs/img/favicon.png -------------------------------------------------------------------------------- /docs/en/docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | 3 | Thanks for considering contributing to **FastOpenAPI** 🎉 4 | This guide explains how to get started, make contributions, write commits, open pull requests, and run tests. 5 | 6 | --- 7 | 8 | ## Setup & Running 9 | 10 | Install dependencies with: 11 | 12 | ```bash 13 | # Fork the repo on GitHub first, then: 14 | git clone https://github.com/yourusername/fastopenapi.git 15 | cd fastopenapi 16 | poetry install 17 | ``` 18 | 19 | If you don't use `poetry`, you can install manually from `pyproject.toml`: 20 | 21 | ```bash 22 | pip install -e .[dev] 23 | ``` 24 | 25 | --- 26 | 27 | ## Project Structure 28 | 29 | - `fastopenapi/` — core library 30 | - `examples/` — examples for different frameworks 31 | - `tests/` — tests for each supported framework 32 | - `benchmarks/` — performance comparisons 33 | - `docs/` — documentation in multiple languages 34 | 35 | --- 36 | 37 | ## Running Tests 38 | 39 | To run tests: 40 | 41 | ```bash 42 | pytest 43 | ``` 44 | 45 | Tests cover both internal logic and integration with aiohttp, flask, sanic, and more. 46 | 47 | --- 48 | 49 | ## Code Style 50 | 51 | This project uses: 52 | 53 | - `black` — code formatting 54 | - `flake8` — linting 55 | - `isort` — import sorting 56 | - `pre-commit` — hooks for clean code before commit 57 | 58 | Install pre-commit hooks: 59 | 60 | ```bash 61 | pre-commit install 62 | ``` 63 | 64 | Run all checks manually: 65 | 66 | ```bash 67 | pre-commit run --all-files 68 | ``` 69 | 70 | --- 71 | 72 | ## Git & Pull Requests 73 | 74 | 1. Fork the repository 75 | 2. Create a new branch: `feature/your-feature` or `fix/your-fix` 76 | 3. Keep commits atomic and meaningful 77 | 4. Open a PR with a clear description: 78 | - What was added/changed? 79 | - Which frameworks were affected? 80 | - How was it tested? 81 | 82 | --- 83 | 84 | ## Documentation 85 | 86 | If your change affects public APIs or behavior, update the relevant documentation: 87 | - `docs/en/` 88 | - If possible, update other languages too (optional) 89 | 90 | --- 91 | 92 | ## Feedback 93 | 94 | Not sure how to implement a feature? 95 | Feel free to open an issue with your proposal. We’re happy to discuss it with you! 96 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/aiohttp.md: -------------------------------------------------------------------------------- 1 | # AIOHTTP Integration 2 | 3 | This guide covers how to use FastOpenAPI with **AIOHTTP** (an asynchronous HTTP framework). 4 | 5 | AIOHTTP applications are usually built with an `aiohttp.web.Application` and run using `aiohttp.web.run_app`. FastOpenAPI provides an `AioHttpRouter` to integrate with this. 6 | 7 | ## Setup 8 | 9 | Make sure you have installed FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[aiohttp] 16 | ``` 17 | 18 | ## Hello World 19 | ```python 20 | from aiohttp import web 21 | from pydantic import BaseModel 22 | from fastopenapi.routers import AioHttpRouter 23 | 24 | app = web.Application() 25 | router = AioHttpRouter(app=app) 26 | 27 | class HelloResponse(BaseModel): 28 | message: str 29 | 30 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 31 | async def hello(name: str): 32 | """Say hello from AIOHTTP""" 33 | return HelloResponse(message=f"Hello, {name}! It's aiohttp!") 34 | 35 | if __name__ == "__main__": 36 | web.run_app(app, host="127.0.0.1", port=8000) 37 | 38 | ``` 39 | 40 | ## Project Example 41 | 42 | See example for this framework in the [`examples/aiohttp/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/aiohttp) directory of the repository. 43 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/falcon.md: -------------------------------------------------------------------------------- 1 | # Falcon Integration 2 | 3 | This guide covers using FastOpenAPI with **Falcon**, a high-performance web framework. 4 | 5 | FastOpenAPI's `FalconRouter` supports Falcon, particularly via Falcon's ASGI interface for asynchronous operation. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[falcon] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import falcon.asgi 22 | import uvicorn 23 | from pydantic import BaseModel 24 | from fastopenapi.routers import FalconRouter 25 | 26 | app = falcon.asgi.App() # Falcon ASGI app (for async support) 27 | router = FalconRouter(app=app) # Attach FastOpenAPI router 28 | 29 | class HelloResponse(BaseModel): 30 | message: str 31 | 32 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 33 | async def hello(name: str): 34 | """Say hello from Falcon""" 35 | return HelloResponse(message=f"Hello, {name}! It's Falcon!") 36 | 37 | if __name__ == "__main__": 38 | uvicorn.run(app, host="127.0.0.1", port=8000) 39 | 40 | ``` 41 | 42 | ## Project Example 43 | 44 | See example for this framework in the [`examples/falcon/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/falcon) directory of the repository. 45 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/flask.md: -------------------------------------------------------------------------------- 1 | # Flask Integration 2 | 3 | This guide demonstrates FastOpenAPI integration with **Flask**, one of the most popular Python web frameworks. 4 | 5 | The `FlaskRouter` class ties FastOpenAPI into the Flask routing system. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[flask] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from flask import Flask 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import FlaskRouter 24 | 25 | app = Flask(__name__) 26 | router = FlaskRouter(app=app) # Attach FastOpenAPI to the Flask app 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | def hello(name: str): 33 | """Say hello from Flask""" 34 | return HelloResponse(message=f"Hello, {name}! It's Flask!") 35 | 36 | if __name__ == "__main__": 37 | app.run(port=8000) 38 | 39 | ``` 40 | 41 | ## Project Example 42 | 43 | See example for this framework in the [`examples/flask/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/flask) directory of the repository. 44 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/quart.md: -------------------------------------------------------------------------------- 1 | # Quart Integration 2 | 3 | Quart is an async web framework with a Flask-like API (it’s a drop-in replacement for Flask but with async support). 4 | 5 | FastOpenAPI provides `QuartRouter` for integrating with Quart. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[quart] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from quart import Quart 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import QuartRouter 24 | 25 | app = Quart(__name__) 26 | router = QuartRouter(app=app) 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | async def hello(name: str): 33 | """Say hello from Quart""" 34 | return HelloResponse(message=f"Hello, {name}! It's Quart!") 35 | 36 | if __name__ == "__main__": 37 | app.run(port=8000) 38 | 39 | ``` 40 | 41 | ## Project Example 42 | 43 | See example for this framework in the [`examples/quart/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/quart) directory of the repository. 44 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/sanic.md: -------------------------------------------------------------------------------- 1 | # Sanic Integration 2 | 3 | Sanic is an async web framework known for its speed. 4 | 5 | FastOpenAPI can integrate with Sanic through the `SanicRouter`. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[sanic] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from sanic import Sanic 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import SanicRouter 24 | 25 | app = Sanic("MySanicApp") 26 | router = SanicRouter(app=app) 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | async def hello(name: str): 33 | """Say hello from Sanic""" 34 | return HelloResponse(message=f"Hello, {name}! It's Sanic!") 35 | 36 | if __name__ == "__main__": 37 | app.run(host="0.0.0.0", port=8000) 38 | 39 | ``` 40 | 41 | ## Project Example 42 | 43 | See example for this framework in the [`examples/sanic/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/sanic) directory of the repository. 44 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/starlette.md: -------------------------------------------------------------------------------- 1 | # Starlette Integration 2 | 3 | Starlette is a lightweight ASGI framework which FastAPI itself is built upon. 4 | 5 | FastOpenAPI's `StarletteRouter` allows you to use FastOpenAPI directly with Starlette applications. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[starlette] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import uvicorn 22 | from pydantic import BaseModel 23 | from starlette.applications import Starlette 24 | from fastopenapi.routers import StarletteRouter 25 | 26 | app = Starlette() 27 | router = StarletteRouter(app=app) 28 | 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Say hello from Starlette""" 37 | return HelloResponse(message=f"Hello, {name}! It's Starlette!") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | 42 | ``` 43 | 44 | ## Project Example 45 | 46 | See example for this framework in the [`examples/starlette/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/starlette) directory of the repository. 47 | -------------------------------------------------------------------------------- /docs/en/docs/frameworks/tornado.md: -------------------------------------------------------------------------------- 1 | # Tornado Integration 2 | 3 | Tornado is a mature Python web framework and asynchronous networking library. 4 | 5 | FastOpenAPI supports Tornado via the `TornadoRouter`, allowing you to add OpenAPI documentation to Tornado applications. 6 | 7 | ## Setup 8 | 9 | Install FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | or 14 | ```bash 15 | pip install fastopenapi[tornado] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import asyncio 22 | from pydantic import BaseModel 23 | from tornado.web import Application 24 | from fastopenapi.routers import TornadoRouter 25 | 26 | app = Application() 27 | router = TornadoRouter(app=app) 28 | 29 | class HelloResponse(BaseModel): 30 | message: str 31 | 32 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 33 | def hello(name: str): 34 | """Say hello from Tornado""" 35 | return HelloResponse(message=f"Hello, {name}! It's Tornado!") 36 | 37 | async def main(): 38 | app.listen(8000) 39 | await asyncio.Event().wait() # Keep running 40 | 41 | if __name__ == "__main__": 42 | asyncio.run(main()) 43 | 44 | ``` 45 | 46 | ## Project Example 47 | 48 | See example for this framework in the [`examples/tornado/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/tornado) directory of the repository. 49 | -------------------------------------------------------------------------------- /docs/en/docs/getting_started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | FastOpenAPI is available on PyPI and supports **Python 3.10+**. You can install the core library or include optional dependencies for specific web frameworks. 4 | 5 | ## Prerequisites 6 | 7 | - **Python 3.10 or higher** – FastOpenAPI requires Python 3.10+ due to its use of modern typing features and Pydantic v2. 8 | - (Optional) An existing web framework (such as Flask, Starlette, etc.) if you plan to integrate with one. If you don't have the framework installed, using the appropriate extra in pip (as shown below) will install it for you. 9 | 10 | ## Using pip 11 | 12 | #### Install only FastOpenAPI 13 | *The ordinary installation* 14 | ```bash 15 | pip install fastopenapi 16 | ``` 17 | 18 | #### Install FastOpenAPI with a specific framework 19 | *Useful if you're starting a new service and haven't installed a framework yet* 20 | ```bash 21 | pip install fastopenapi[aiohttp] 22 | ``` 23 | ```bash 24 | pip install fastopenapi[falcon] 25 | ``` 26 | ```bash 27 | pip install fastopenapi[flask] 28 | ``` 29 | ```bash 30 | pip install fastopenapi[quart] 31 | ``` 32 | ```bash 33 | pip install fastopenapi[sanic] 34 | ``` 35 | ```bash 36 | pip install fastopenapi[starlette] 37 | ``` 38 | ```bash 39 | pip install fastopenapi[tornado] 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /docs/en/docs/img/HelloRedoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/en/docs/img/HelloRedoc.jpg -------------------------------------------------------------------------------- /docs/en/docs/img/HelloSwagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/en/docs/img/HelloSwagger.jpg -------------------------------------------------------------------------------- /docs/en/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/en/docs/img/favicon.png -------------------------------------------------------------------------------- /docs/es/docs/frameworks/aiohttp.md: -------------------------------------------------------------------------------- 1 | # Integración con AIOHTTP 2 | 3 | Esta guía explica cómo utilizar FastOpenAPI con **AIOHTTP** (un framework HTTP asíncrono). 4 | 5 | Las aplicaciones con AIOHTTP se construyen típicamente con `aiohttp.web.Application` y se ejecutan usando `aiohttp.web.run_app`. FastOpenAPI proporciona `AioHttpRouter` para integrarse con este framework. 6 | 7 | ## Instalación 8 | 9 | Asegúrate de tener instalado FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[aiohttp] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from aiohttp import web 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import AioHttpRouter 26 | 27 | app = web.Application() 28 | router = AioHttpRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Saludo desde AIOHTTP""" 36 | return HelloResponse(message=f"Hola, {name}! Esto es aiohttp!") 37 | 38 | if __name__ == "__main__": 39 | web.run_app(app, host="127.0.0.1", port=8000) 40 | ``` 41 | 42 | ## Proyecto de ejemplo 43 | 44 | Puedes encontrar un ejemplo funcional en el directorio [`examples/aiohttp/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/aiohttp) del repositorio. 45 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/falcon.md: -------------------------------------------------------------------------------- 1 | # Integración con Falcon 2 | 3 | Esta guía explica cómo usar FastOpenAPI con **Falcon**, un framework web de alto rendimiento. 4 | 5 | El `FalconRouter` de FastOpenAPI es compatible con Falcon, especialmente usando su interfaz ASGI para soporte asíncrono. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[falcon] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import falcon.asgi 24 | import uvicorn 25 | from pydantic import BaseModel 26 | from fastopenapi.routers import FalconRouter 27 | 28 | app = falcon.asgi.App() 29 | router = FalconRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Saludo desde Falcon""" 37 | return HelloResponse(message=f"Hola, {name}! Esto es Falcon!") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Proyecto de ejemplo 44 | 45 | Consulta el directorio [`examples/falcon/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/falcon) para un ejemplo funcional. 46 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/flask.md: -------------------------------------------------------------------------------- 1 | # Integración con Flask 2 | 3 | Esta guía muestra cómo integrar FastOpenAPI con **Flask**, uno de los frameworks web más populares de Python. 4 | 5 | La clase `FlaskRouter` conecta FastOpenAPI con el sistema de rutas de Flask. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[flask] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from flask import Flask 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import FlaskRouter 26 | 27 | app = Flask(__name__) 28 | router = FlaskRouter(app=app) # Enlazar con FastOpenAPI 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | def hello(name: str): 35 | """Saludo desde Flask""" 36 | return HelloResponse(message=f"Hola, {name}! Esto es Flask!") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Proyecto de ejemplo 43 | 44 | Consulta el directorio [`examples/flask/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/flask) para ver un ejemplo funcional. 45 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/quart.md: -------------------------------------------------------------------------------- 1 | # Integración con Quart 2 | 3 | Quart es un framework web asíncrono con una API muy similar a Flask (es un reemplazo directo compatible con async). 4 | 5 | FastOpenAPI proporciona `QuartRouter` para integrarse con aplicaciones construidas en Quart. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[quart] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from quart import Quart 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import QuartRouter 26 | 27 | app = Quart(__name__) 28 | router = QuartRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Saludo desde Quart""" 36 | return HelloResponse(message=f"Hola, {name}! Esto es Quart!") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Proyecto de ejemplo 43 | 44 | Consulta el directorio [`examples/quart/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/quart) para ver un ejemplo completo. 45 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/sanic.md: -------------------------------------------------------------------------------- 1 | # Integración con Sanic 2 | 3 | Sanic es un framework web asíncrono conocido por su alto rendimiento. 4 | 5 | FastOpenAPI se integra con Sanic a través de `SanicRouter`. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[sanic] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from sanic import Sanic 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import SanicRouter 26 | 27 | app = Sanic("MySanicApp") 28 | router = SanicRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Saludo desde Sanic""" 36 | return HelloResponse(message=f"Hola, {name}! Esto es Sanic!") 37 | 38 | if __name__ == "__main__": 39 | app.run(host="0.0.0.0", port=8000) 40 | ``` 41 | 42 | ## Proyecto de ejemplo 43 | 44 | Puedes consultar el directorio [`examples/sanic/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/sanic) para ver un ejemplo funcional. 45 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/starlette.md: -------------------------------------------------------------------------------- 1 | # Integración con Starlette 2 | 3 | Starlette es un framework ASGI liviano sobre el que está construido FastAPI. 4 | 5 | `StarletteRouter` de FastOpenAPI permite utilizar FastOpenAPI directamente en aplicaciones Starlette. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o: 15 | 16 | ```bash 17 | pip install fastopenapi[starlette] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import uvicorn 24 | from pydantic import BaseModel 25 | from starlette.applications import Starlette 26 | from fastopenapi.routers import StarletteRouter 27 | 28 | app = Starlette() 29 | router = StarletteRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Saludo desde Starlette""" 37 | return HelloResponse(message=f"Hola, {name}! Esto es Starlette!") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Proyecto de ejemplo 44 | 45 | Consulta el directorio [`examples/starlette/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/starlette) para ver un ejemplo funcional. 46 | -------------------------------------------------------------------------------- /docs/es/docs/frameworks/tornado.md: -------------------------------------------------------------------------------- 1 | # Integración con Tornado 2 | 3 | Tornado es un framework web maduro y una biblioteca de red asíncrona para Python. 4 | 5 | FastOpenAPI es compatible con Tornado mediante `TornadoRouter`, lo que permite añadir documentación OpenAPI a las aplicaciones Tornado. 6 | 7 | ## Instalación 8 | 9 | Instala FastOpenAPI: 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | o 15 | 16 | ```bash 17 | pip install fastopenapi[tornado] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import asyncio 24 | from pydantic import BaseModel 25 | from tornado.web import Application 26 | from fastopenapi.routers import TornadoRouter 27 | 28 | app = Application() 29 | router = TornadoRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | def hello(name: str): 36 | """Saludo desde Tornado""" 37 | return HelloResponse(message=f"Hola, {name}! Esto es Tornado!") 38 | 39 | async def main(): 40 | app.listen(8000) 41 | await asyncio.Event().wait() 42 | 43 | if __name__ == "__main__": 44 | asyncio.run(main()) 45 | ``` 46 | 47 | ## Proyecto de ejemplo 48 | 49 | Consulta el directorio [`examples/tornado/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/tornado) del repositorio. 50 | -------------------------------------------------------------------------------- /docs/es/docs/getting_started/installation.md: -------------------------------------------------------------------------------- 1 | # Instalación 2 | 3 | FastOpenAPI está disponible en PyPI y es compatible con **Python 3.10 o superior**. Puedes instalar solo la biblioteca principal o incluir dependencias opcionales para frameworks web específicos. 4 | 5 | ## Requisitos previos 6 | 7 | - **Python 3.10 o superior** – requerido debido al uso de tipado moderno y Pydantic v2. 8 | - (Opcional) Un framework web existente (como Flask, Starlette, etc.), si planeas integrarlo. Si no lo tienes instalado, puedes usar los extras para instalarlo junto con FastOpenAPI. 9 | 10 | ## Instalación con pip 11 | 12 | #### Solo instalar FastOpenAPI 13 | *Instalación básica:* 14 | ```bash 15 | pip install fastopenapi 16 | ``` 17 | 18 | #### Instalar FastOpenAPI con un framework específico 19 | *Útil si comienzas un nuevo servicio sin haber instalado aún el framework:* 20 | ```bash 21 | pip install fastopenapi[aiohttp] 22 | ``` 23 | ```bash 24 | pip install fastopenapi[falcon] 25 | ``` 26 | ```bash 27 | pip install fastopenapi[flask] 28 | ``` 29 | ```bash 30 | pip install fastopenapi[quart] 31 | ``` 32 | ```bash 33 | pip install fastopenapi[sanic] 34 | ``` 35 | ```bash 36 | pip install fastopenapi[starlette] 37 | ``` 38 | ```bash 39 | pip install fastopenapi[tornado] 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /docs/es/docs/img/HelloRedoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/es/docs/img/HelloRedoc.jpg -------------------------------------------------------------------------------- /docs/es/docs/img/HelloSwagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/es/docs/img/HelloSwagger.jpg -------------------------------------------------------------------------------- /docs/es/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/es/docs/img/favicon.png -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/aiohttp.md: -------------------------------------------------------------------------------- 1 | # Intégration AIOHTTP 2 | 3 | Ce guide explique comment utiliser FastOpenAPI avec **AIOHTTP**, un framework HTTP asynchrone pour Python. 4 | 5 | Les applications AIOHTTP sont construites avec `aiohttp.web.Application` et exécutées avec `aiohttp.web.run_app`. FastOpenAPI fournit le routeur `AioHttpRouter` pour s’y intégrer. 6 | 7 | ## Installation 8 | 9 | Assurez-vous que FastOpenAPI est installé : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[aiohttp] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from aiohttp import web 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import AioHttpRouter 26 | 27 | app = web.Application() 28 | router = AioHttpRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Dire bonjour avec AIOHTTP""" 36 | return HelloResponse(message=f"Bonjour, {name} ! C'est aiohttp !") 37 | 38 | if __name__ == "__main__": 39 | web.run_app(app, host="127.0.0.1", port=8000) 40 | ``` 41 | 42 | ## Exemple de projet 43 | 44 | Un exemple complet est disponible dans le répertoire [`examples/aiohttp/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/aiohttp) du dépôt. 45 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/falcon.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Falcon 2 | 3 | Ce guide présente l’utilisation de FastOpenAPI avec **Falcon**, un framework web haute performance. 4 | 5 | Le `FalconRouter` de FastOpenAPI prend en charge Falcon, notamment via son interface ASGI pour les opérations asynchrones. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[falcon] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import falcon.asgi 24 | import uvicorn 25 | from pydantic import BaseModel 26 | from fastopenapi.routers import FalconRouter 27 | 28 | app = falcon.asgi.App() 29 | router = FalconRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Dire bonjour avec Falcon""" 37 | return HelloResponse(message=f"Bonjour, {name} ! C’est Falcon !") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Exemple de projet 44 | 45 | Un exemple complet est disponible dans le dossier [`examples/falcon/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/falcon) du dépôt. 46 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/flask.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Flask 2 | 3 | Ce guide explique comment intégrer FastOpenAPI avec **Flask**, l’un des frameworks web Python les plus populaires. 4 | 5 | La classe `FlaskRouter` permet de lier FastOpenAPI au système de routage de Flask. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[flask] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from flask import Flask 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import FlaskRouter 26 | 27 | app = Flask(__name__) 28 | router = FlaskRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | def hello(name: str): 35 | """Dire bonjour avec Flask""" 36 | return HelloResponse(message=f"Bonjour, {name} ! C’est Flask !") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Exemple de projet 43 | 44 | Un exemple complet est disponible dans le dossier [`examples/flask/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/flask) du dépôt. 45 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/quart.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Quart 2 | 3 | Quart est un framework web asynchrone avec une API très proche de Flask (c’est un remplacement direct avec support async). 4 | 5 | FastOpenAPI fournit `QuartRouter` pour une intégration fluide avec Quart. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[quart] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from quart import Quart 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import QuartRouter 26 | 27 | app = Quart(__name__) 28 | router = QuartRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Dire bonjour avec Quart""" 36 | return HelloResponse(message=f"Bonjour, {name} ! C’est Quart !") 37 | 38 | if __name__ == "__main__": 39 | app.run(port=8000) 40 | ``` 41 | 42 | ## Exemple de projet 43 | 44 | Consultez le dossier [`examples/quart/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/quart) pour un exemple complet. 45 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/sanic.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Sanic 2 | 3 | Sanic est un framework web asynchrone connu pour sa rapidité. 4 | 5 | FastOpenAPI s’intègre à Sanic via le `SanicRouter`. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[sanic] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | from sanic import Sanic 24 | from pydantic import BaseModel 25 | from fastopenapi.routers import SanicRouter 26 | 27 | app = Sanic("MySanicApp") 28 | router = SanicRouter(app=app) 29 | 30 | class HelloResponse(BaseModel): 31 | message: str 32 | 33 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 34 | async def hello(name: str): 35 | """Dire bonjour avec Sanic""" 36 | return HelloResponse(message=f"Bonjour, {name} ! C’est Sanic !") 37 | 38 | if __name__ == "__main__": 39 | app.run(host="0.0.0.0", port=8000) 40 | ``` 41 | 42 | ## Exemple de projet 43 | 44 | Consultez le dossier [`examples/sanic/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/sanic) pour un exemple complet. 45 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/starlette.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Starlette 2 | 3 | Starlette est un framework ASGI léger sur lequel repose FastAPI lui-même. 4 | 5 | Le `StarletteRouter` de FastOpenAPI permet une intégration directe avec les applications Starlette. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou : 15 | 16 | ```bash 17 | pip install fastopenapi[starlette] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import uvicorn 24 | from pydantic import BaseModel 25 | from starlette.applications import Starlette 26 | from fastopenapi.routers import StarletteRouter 27 | 28 | app = Starlette() 29 | router = StarletteRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | async def hello(name: str): 36 | """Dire bonjour avec Starlette""" 37 | return HelloResponse(message=f"Bonjour, {name} ! C’est Starlette !") 38 | 39 | if __name__ == "__main__": 40 | uvicorn.run(app, host="127.0.0.1", port=8000) 41 | ``` 42 | 43 | ## Exemple de projet 44 | 45 | Consultez le dossier [`examples/starlette/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/starlette) pour un exemple complet. 46 | -------------------------------------------------------------------------------- /docs/fr/docs/frameworks/tornado.md: -------------------------------------------------------------------------------- 1 | # Intégration avec Tornado 2 | 3 | Tornado est un framework web Python mature et une bibliothèque réseau asynchrone. 4 | 5 | FastOpenAPI prend en charge Tornado via `TornadoRouter`, ce qui permet d’ajouter de la documentation OpenAPI aux applications Tornado. 6 | 7 | ## Installation 8 | 9 | Installez FastOpenAPI : 10 | 11 | ```bash 12 | pip install fastopenapi 13 | ``` 14 | ou 15 | 16 | ```bash 17 | pip install fastopenapi[tornado] 18 | ``` 19 | 20 | ## Hello World 21 | 22 | ```python 23 | import asyncio 24 | from pydantic import BaseModel 25 | from tornado.web import Application 26 | from fastopenapi.routers import TornadoRouter 27 | 28 | app = Application() 29 | router = TornadoRouter(app=app) 30 | 31 | class HelloResponse(BaseModel): 32 | message: str 33 | 34 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 35 | def hello(name: str): 36 | """Dire bonjour avec Tornado""" 37 | return HelloResponse(message=f"Bonjour, {name} ! C’est Tornado !") 38 | 39 | async def main(): 40 | app.listen(8000) 41 | await asyncio.Event().wait() 42 | 43 | if __name__ == "__main__": 44 | asyncio.run(main()) 45 | ``` 46 | 47 | ## Exemple de projet 48 | 49 | Un exemple complet est disponible dans le dossier [`examples/tornado/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/tornado) du dépôt. 50 | -------------------------------------------------------------------------------- /docs/fr/docs/getting_started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | FastOpenAPI est disponible sur PyPI et prend en charge **Python 3.10 ou supérieur**. Vous pouvez installer uniquement la bibliothèque principale ou inclure des dépendances optionnelles pour certains frameworks web. 4 | 5 | ## Prérequis 6 | 7 | - **Python 3.10 ou plus récent** – requis en raison de l'utilisation des fonctionnalités modernes de typage et de Pydantic v2. 8 | - (Optionnel) Un framework web existant (comme Flask, Starlette, etc.) si vous prévoyez une intégration. Si vous ne l'avez pas encore installé, vous pouvez utiliser les extras de `pip` pour l’installer automatiquement avec FastOpenAPI. 9 | 10 | ## Utilisation de pip 11 | 12 | #### Installer uniquement FastOpenAPI 13 | *Installation standard :* 14 | ```bash 15 | pip install fastopenapi 16 | ``` 17 | 18 | #### Installer FastOpenAPI avec un framework spécifique 19 | *Utile si vous commencez un nouveau projet sans framework installé :* 20 | ```bash 21 | pip install fastopenapi[aiohttp] 22 | ``` 23 | ```bash 24 | pip install fastopenapi[falcon] 25 | ``` 26 | ```bash 27 | pip install fastopenapi[flask] 28 | ``` 29 | ```bash 30 | pip install fastopenapi[quart] 31 | ``` 32 | ```bash 33 | pip install fastopenapi[sanic] 34 | ``` 35 | ```bash 36 | pip install fastopenapi[starlette] 37 | ``` 38 | ```bash 39 | pip install fastopenapi[tornado] 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /docs/fr/docs/img/HelloRedoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/fr/docs/img/HelloRedoc.jpg -------------------------------------------------------------------------------- /docs/fr/docs/img/HelloSwagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/fr/docs/img/HelloSwagger.jpg -------------------------------------------------------------------------------- /docs/fr/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/fr/docs/img/favicon.png -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/aiohttp.md: -------------------------------------------------------------------------------- 1 | # Интеграция с AIOHTTP 2 | 3 | Это руководство описывает, как использовать FastOpenAPI с **AIOHTTP** (асинхронным HTTP-фреймворком). 4 | 5 | Приложения на AIOHTTP обычно создаются с использованием `aiohttp.web.Application` и запускаются через `aiohttp.web.run_app`. FastOpenAPI предоставляет `AioHttpRouter` для интеграции с этим фреймворком. 6 | 7 | ## Установка 8 | 9 | Убедитесь, что установлен FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[aiohttp] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from aiohttp import web 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import AioHttpRouter 24 | 25 | app = web.Application() 26 | router = AioHttpRouter(app=app) 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | async def hello(name: str): 33 | """Say hello from AIOHTTP""" 34 | return HelloResponse(message=f"Hello, {name}! It's aiohttp!") 35 | 36 | if __name__ == "__main__": 37 | web.run_app(app, host="127.0.0.1", port=8000) 38 | ``` 39 | 40 | ## Пример проекта 41 | 42 | См. пример для этого фреймворка в директории [`examples/aiohttp/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/aiohttp) репозитория. 43 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/falcon.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Falcon 2 | 3 | Это руководство описывает использование FastOpenAPI с **Falcon**, высокопроизводительным веб-фреймворком. 4 | 5 | Роутер `FalconRouter` от FastOpenAPI поддерживает Falcon, особенно через ASGI-интерфейс для асинхронной работы. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[falcon] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import falcon.asgi 22 | import uvicorn 23 | from pydantic import BaseModel 24 | from fastopenapi.routers import FalconRouter 25 | 26 | app = falcon.asgi.App() # ASGI-приложение Falcon (для поддержки async) 27 | router = FalconRouter(app=app) # Подключение роутера FastOpenAPI 28 | 29 | class HelloResponse(BaseModel): 30 | message: str 31 | 32 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 33 | async def hello(name: str): 34 | """Say hello from Falcon""" 35 | return HelloResponse(message=f"Hello, {name}! It's Falcon!") 36 | 37 | if __name__ == "__main__": 38 | uvicorn.run(app, host="127.0.0.1", port=8000) 39 | ``` 40 | 41 | ## Пример проекта 42 | 43 | См. пример для этого фреймворка в директории [`examples/falcon/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/falcon) репозитория. 44 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/flask.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Flask 2 | 3 | Это руководство демонстрирует, как интегрировать FastOpenAPI с **Flask** — одним из самых популярных веб-фреймворков на Python. 4 | 5 | Класс `FlaskRouter` связывает FastOpenAPI с системой маршрутизации Flask. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[flask] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from flask import Flask 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import FlaskRouter 24 | 25 | app = Flask(__name__) 26 | router = FlaskRouter(app=app) # Подключение FastOpenAPI к Flask 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | def hello(name: str): 33 | """Say hello from Flask""" 34 | return HelloResponse(message=f"Hello, {name}! It's Flask!") 35 | 36 | if __name__ == "__main__": 37 | app.run(port=8000) 38 | ``` 39 | 40 | ## Пример проекта 41 | 42 | См. пример использования этого фреймворка в директории [`examples/flask/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/flask) репозитория. 43 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/quart.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Quart 2 | 3 | Quart — это асинхронный веб-фреймворк с API, схожим с Flask (может быть прямой заменой Flask с поддержкой async). 4 | 5 | FastOpenAPI предоставляет `QuartRouter` для интеграции с Quart. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[quart] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from quart import Quart 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import QuartRouter 24 | 25 | app = Quart(__name__) 26 | router = QuartRouter(app=app) 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | async def hello(name: str): 33 | """Say hello from Quart""" 34 | return HelloResponse(message=f"Hello, {name}! It's Quart!") 35 | 36 | if __name__ == "__main__": 37 | app.run(port=8000) 38 | ``` 39 | 40 | ## Пример проекта 41 | 42 | См. пример использования этого фреймворка в директории [`examples/quart/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/quart) репозитория. 43 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/sanic.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Sanic 2 | 3 | Sanic — это асинхронный веб-фреймворк, известный своей высокой производительностью. 4 | 5 | FastOpenAPI интегрируется с Sanic с помощью `SanicRouter`. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[sanic] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | from sanic import Sanic 22 | from pydantic import BaseModel 23 | from fastopenapi.routers import SanicRouter 24 | 25 | app = Sanic("MySanicApp") 26 | router = SanicRouter(app=app) 27 | 28 | class HelloResponse(BaseModel): 29 | message: str 30 | 31 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 32 | async def hello(name: str): 33 | """Say hello from Sanic""" 34 | return HelloResponse(message=f"Hello, {name}! It's Sanic!") 35 | 36 | if __name__ == "__main__": 37 | app.run(host="0.0.0.0", port=8000) 38 | ``` 39 | 40 | ## Пример проекта 41 | 42 | См. пример для этого фреймворка в директории [`examples/sanic/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/sanic) репозитория. 43 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/starlette.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Starlette 2 | 3 | Starlette — это лёгкий ASGI-фреймворк, на базе которого построен сам FastAPI. 4 | 5 | `StarletteRouter` от FastOpenAPI позволяет использовать FastOpenAPI напрямую в приложениях Starlette. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[starlette] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import uvicorn 22 | from pydantic import BaseModel 23 | from starlette.applications import Starlette 24 | from fastopenapi.routers import StarletteRouter 25 | 26 | app = Starlette() 27 | router = StarletteRouter(app=app) 28 | 29 | class HelloResponse(BaseModel): 30 | message: str 31 | 32 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 33 | async def hello(name: str): 34 | """Say hello from Starlette""" 35 | return HelloResponse(message=f"Hello, {name}! It's Starlette!") 36 | 37 | if __name__ == "__main__": 38 | uvicorn.run(app, host="127.0.0.1", port=8000) 39 | ``` 40 | 41 | ## Пример проекта 42 | 43 | См. пример для этого фреймворка в директории [`examples/starlette/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/starlette) репозитория. 44 | -------------------------------------------------------------------------------- /docs/ru/docs/frameworks/tornado.md: -------------------------------------------------------------------------------- 1 | # Интеграция с Tornado 2 | 3 | Tornado — зрелый веб-фреймворк и асинхронная сетевая библиотека на Python. 4 | 5 | FastOpenAPI поддерживает Tornado через `TornadoRouter`, позволяя добавлять документацию OpenAPI к приложениям на Tornado. 6 | 7 | ## Установка 8 | 9 | Установите FastOpenAPI: 10 | ```bash 11 | pip install fastopenapi 12 | ``` 13 | или 14 | ```bash 15 | pip install fastopenapi[tornado] 16 | ``` 17 | 18 | ## Hello World 19 | 20 | ```python 21 | import asyncio 22 | from pydantic import BaseModel 23 | from tornado.web import Application 24 | from fastopenapi.routers import TornadoRouter 25 | 26 | app = Application() 27 | router = TornadoRouter(app=app) 28 | 29 | class HelloResponse(BaseModel): 30 | message: str 31 | 32 | @router.get("/hello", tags=["Hello"], response_model=HelloResponse) 33 | def hello(name: str): 34 | """Say hello from Tornado""" 35 | return HelloResponse(message=f"Hello, {name}! It's Tornado!") 36 | 37 | async def main(): 38 | app.listen(8000) 39 | await asyncio.Event().wait() # Постоянное ожидание 40 | 41 | if __name__ == "__main__": 42 | asyncio.run(main()) 43 | ``` 44 | 45 | ## Пример проекта 46 | 47 | См. пример для этого фреймворка в директории [`examples/tornado/`](https://github.com/mr-fatalyst/fastopenapi/tree/master/examples/tornado) репозитория. 48 | -------------------------------------------------------------------------------- /docs/ru/docs/getting_started/installation.md: -------------------------------------------------------------------------------- 1 | # Установка 2 | 3 | FastOpenAPI доступен на PyPI и поддерживает **Python 3.10+**. Вы можете установить только основную библиотеку или включить дополнительные зависимости для определённых веб-фреймворков. 4 | 5 | ## Предварительные требования 6 | 7 | - **Python 3.10 или новее** – FastOpenAPI требует Python 3.10+ из-за использования современных аннотаций типов и Pydantic v2. 8 | - (Необязательно) Установленный веб-фреймворк (например, Flask, Starlette и др.), если вы планируете интеграцию. Если фреймворк ещё не установлен, использование нужной опции pip (см. ниже) установит его автоматически. 9 | 10 | ## Установка через pip 11 | 12 | #### Установка только FastOpenAPI 13 | *Обычная установка* 14 | ```bash 15 | pip install fastopenapi 16 | ``` 17 | 18 | #### Установка FastOpenAPI с определённым фреймворком 19 | *Полезно, если вы начинаете новый сервис и ещё не установили фреймворк* 20 | ```bash 21 | pip install fastopenapi[aiohttp] 22 | ``` 23 | ```bash 24 | pip install fastopenapi[falcon] 25 | ``` 26 | ```bash 27 | pip install fastopenapi[flask] 28 | ``` 29 | ```bash 30 | pip install fastopenapi[quart] 31 | ``` 32 | ```bash 33 | pip install fastopenapi[sanic] 34 | ``` 35 | ```bash 36 | pip install fastopenapi[starlette] 37 | ``` 38 | ```bash 39 | pip install fastopenapi[tornado] 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /docs/ru/docs/img/HelloRedoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/ru/docs/img/HelloRedoc.jpg -------------------------------------------------------------------------------- /docs/ru/docs/img/HelloSwagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/ru/docs/img/HelloSwagger.jpg -------------------------------------------------------------------------------- /docs/ru/docs/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/docs/ru/docs/img/favicon.png -------------------------------------------------------------------------------- /examples/aiohttp/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/aiohttp/app/__init__.py -------------------------------------------------------------------------------- /examples/aiohttp/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/aiohttp/app/api/__init__.py -------------------------------------------------------------------------------- /examples/aiohttp/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import AioHttpRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = AioHttpRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/aiohttp/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import AioHttpRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = AioHttpRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/aiohttp/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | from fastopenapi.routers import AioHttpRouter 4 | 5 | from ...schemas.authors import ( 6 | AuthorSchema, 7 | CreateAuthorSchema, 8 | FilterAuthorSchema, 9 | UpdateAuthorSchema, 10 | ) 11 | from ...services.authors import AuthorService 12 | 13 | author_service = AuthorService() 14 | 15 | router = AioHttpRouter() 16 | 17 | 18 | @router.post( 19 | "/authors", 20 | tags=["Authors"], 21 | status_code=201, 22 | response_errors=[400, 422, 500], 23 | response_model=AuthorSchema, 24 | ) 25 | async def create_author(body: CreateAuthorSchema) -> AuthorSchema: 26 | return await author_service.create_author(body) 27 | 28 | 29 | @router.get( 30 | "/authors/{author_id}", 31 | tags=["Authors"], 32 | response_errors=[400, 404, 500], 33 | response_model=AuthorSchema, 34 | ) 35 | async def get_author(author_id: int) -> AuthorSchema: 36 | author = await author_service.get_author(author_id) 37 | if not author: 38 | raise web.HTTPNotFound(reason="Author not found") 39 | return author 40 | 41 | 42 | @router.get( 43 | "/authors/", 44 | tags=["Authors"], 45 | response_errors=[500], 46 | response_model=list[AuthorSchema], 47 | ) 48 | async def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 49 | return await author_service.get_authors(body) 50 | 51 | 52 | @router.delete( 53 | "/authors/{author_id}", 54 | tags=["Authors"], 55 | status_code=204, 56 | response_errors=[400, 404, 500], 57 | ) 58 | async def delete_author(author_id: int) -> None: 59 | author = await author_service.delete_author(author_id) 60 | if not author: 61 | raise web.HTTPNotFound(reason="Author not found") 62 | 63 | 64 | @router.patch( 65 | "/authors/{author_id}", 66 | tags=["Authors"], 67 | response_errors=[400, 404, 422, 500], 68 | response_model=AuthorSchema, 69 | ) 70 | async def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 71 | author = await author_service.update_author(author_id, body) 72 | if not author: 73 | raise web.HTTPNotFound(reason="Author not found") 74 | return author 75 | -------------------------------------------------------------------------------- /examples/aiohttp/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | from fastopenapi.routers import AioHttpRouter 4 | 5 | from ...schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ...services.posts import PostService 12 | 13 | post_service = PostService() 14 | router = AioHttpRouter() 15 | 16 | 17 | @router.post( 18 | "/posts", 19 | tags=["Posts"], 20 | status_code=201, 21 | response_errors=[400, 422, 500], 22 | response_model=PostSchema, 23 | ) 24 | async def create_post(body: CreatePostSchema) -> PostSchema: 25 | return await post_service.create_post(body) 26 | 27 | 28 | @router.get( 29 | "/posts/{post_id}", 30 | tags=["Posts"], 31 | response_errors=[400, 404, 500], 32 | response_model=PostSchema, 33 | ) 34 | async def get_post(post_id: int) -> PostSchema: 35 | post = await post_service.get_post(post_id) 36 | if not post: 37 | raise web.HTTPNotFound(reason="Author not found") 38 | return post 39 | 40 | 41 | @router.get( 42 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 43 | ) 44 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 45 | return await post_service.get_posts(body) 46 | 47 | 48 | @router.delete( 49 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 50 | ) 51 | async def delete_post(post_id: int) -> None: 52 | post = await post_service.delete_post(post_id) 53 | if not post: 54 | raise web.HTTPNotFound(reason="Post not found") 55 | 56 | 57 | @router.patch( 58 | "/posts/{post_id}", 59 | tags=["Posts"], 60 | response_errors=[400, 404, 422, 500], 61 | response_model=PostSchema, 62 | ) 63 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 64 | post = await post_service.update_post(post_id, body) 65 | if not post: 66 | raise web.HTTPNotFound(reason="Post not found") 67 | return post 68 | -------------------------------------------------------------------------------- /examples/aiohttp/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/aiohttp/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/aiohttp/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/aiohttp/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/aiohttp/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/aiohttp/app/services/__init__.py -------------------------------------------------------------------------------- /examples/aiohttp/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | async def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | async def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | async def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | async def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | async def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/aiohttp/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | from ..schemas.posts import ( 4 | CreatePostSchema, 5 | FilterPostSchema, 6 | PostSchema, 7 | UpdatePostSchema, 8 | ) 9 | from ..storage import authors, posts 10 | 11 | 12 | class PostService: 13 | async def create_post(self, body: CreatePostSchema) -> PostSchema: 14 | post_counter = len(posts) 15 | author = authors.get(body.author_id) 16 | if not author: 17 | raise web.HTTPNotFound(reason="Author not found") 18 | 19 | data = { 20 | "id": post_counter, 21 | "title": body.title, 22 | "content": body.content, 23 | "author_id": body.author_id, 24 | } 25 | post = PostSchema(**data) 26 | posts[post_counter] = post 27 | return post 28 | 29 | async def get_post(self, post_id: int) -> PostSchema | None: 30 | return posts.get(post_id) 31 | 32 | async def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 33 | def match(post: PostSchema) -> bool: 34 | for field, value in body.model_dump(exclude_none=True).items(): 35 | if getattr(post, field) != value: 36 | return False 37 | return True 38 | 39 | return [item for _, item in posts.items() if match(item)] 40 | 41 | async def update_post( 42 | self, post_id: int, body: UpdatePostSchema 43 | ) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | async def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/aiohttp/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/aiohttp/run.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from app.api.routes import api_router 3 | 4 | from fastopenapi.routers import AioHttpRouter 5 | 6 | app = web.Application() 7 | 8 | router = AioHttpRouter( 9 | app=app, 10 | title="MyAioHttpApp", 11 | version="0.0.1", 12 | docs_url="/docs", 13 | redoc_url="/redoc", 14 | openapi_version="3.0.0", 15 | ) 16 | router.include_router(api_router, prefix="/api") 17 | 18 | 19 | if __name__ == "__main__": 20 | web.run_app(app, host="127.0.0.1", port=8000) 21 | -------------------------------------------------------------------------------- /examples/aiohttp_example.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from pydantic import BaseModel 3 | 4 | from fastopenapi.routers import AioHttpRouter 5 | 6 | app = web.Application() 7 | router = AioHttpRouter(app=app) 8 | 9 | 10 | class HelloResponse(BaseModel): 11 | message: str 12 | 13 | 14 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 15 | async def hello(name: str): 16 | """Say hello from aiohttp""" 17 | return HelloResponse(message=f"Hello, {name}! It's aiohttp!") 18 | 19 | 20 | if __name__ == "__main__": 21 | web.run_app(app, host="127.0.0.1", port=8000) 22 | -------------------------------------------------------------------------------- /examples/falcon/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/falcon/app/__init__.py -------------------------------------------------------------------------------- /examples/falcon/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/falcon/app/api/__init__.py -------------------------------------------------------------------------------- /examples/falcon/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import FalconRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = FalconRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/falcon/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import FalconRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = FalconRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/falcon/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | import falcon 2 | 3 | from fastopenapi.routers import FalconRouter 4 | 5 | from ...schemas.authors import ( 6 | AuthorSchema, 7 | CreateAuthorSchema, 8 | FilterAuthorSchema, 9 | UpdateAuthorSchema, 10 | ) 11 | from ...services.authors import AuthorService 12 | 13 | author_service = AuthorService() 14 | 15 | router = FalconRouter() 16 | 17 | 18 | @router.post( 19 | "/authors", 20 | tags=["Authors"], 21 | status_code=201, 22 | response_errors=[400, 422, 500], 23 | response_model=AuthorSchema, 24 | ) 25 | async def create_author(body: CreateAuthorSchema) -> AuthorSchema: 26 | return await author_service.create_author(body) 27 | 28 | 29 | @router.get( 30 | "/authors/{author_id}", 31 | tags=["Authors"], 32 | response_errors=[400, 404, 500], 33 | response_model=AuthorSchema, 34 | ) 35 | async def get_author(author_id: int) -> AuthorSchema: 36 | author = await author_service.get_author(author_id) 37 | if not author: 38 | raise falcon.HTTPNotFound() 39 | return author 40 | 41 | 42 | @router.get( 43 | "/authors/", 44 | tags=["Authors"], 45 | response_errors=[500], 46 | response_model=list[AuthorSchema], 47 | ) 48 | async def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 49 | return await author_service.get_authors(body) 50 | 51 | 52 | @router.delete( 53 | "/authors/{author_id}", 54 | tags=["Authors"], 55 | status_code=204, 56 | response_errors=[400, 404, 500], 57 | ) 58 | async def delete_author(author_id: int) -> None: 59 | author = await author_service.delete_author(author_id) 60 | if not author: 61 | raise falcon.HTTPNotFound() 62 | 63 | 64 | @router.patch( 65 | "/authors/{author_id}", 66 | tags=["Authors"], 67 | response_errors=[400, 404, 422, 500], 68 | response_model=AuthorSchema, 69 | ) 70 | async def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 71 | author = await author_service.update_author(author_id, body) 72 | if not author: 73 | raise falcon.HTTPNotFound() 74 | return author 75 | -------------------------------------------------------------------------------- /examples/falcon/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | import falcon 2 | 3 | from fastopenapi.routers import FalconRouter 4 | 5 | from ...schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ...services.posts import PostService 12 | 13 | post_service = PostService() 14 | router = FalconRouter() 15 | 16 | 17 | @router.post( 18 | "/posts", 19 | tags=["Posts"], 20 | status_code=201, 21 | response_errors=[400, 422, 500], 22 | response_model=PostSchema, 23 | ) 24 | async def create_post(body: CreatePostSchema) -> PostSchema: 25 | return await post_service.create_post(body) 26 | 27 | 28 | @router.get( 29 | "/posts/{post_id}", 30 | tags=["Posts"], 31 | response_errors=[400, 404, 500], 32 | response_model=PostSchema, 33 | ) 34 | async def get_post(post_id: int) -> PostSchema: 35 | post = await post_service.get_post(post_id) 36 | if not post: 37 | raise falcon.HTTPNotFound() 38 | return post 39 | 40 | 41 | @router.get( 42 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 43 | ) 44 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 45 | return await post_service.get_posts(body) 46 | 47 | 48 | @router.delete( 49 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 50 | ) 51 | async def delete_post(post_id: int) -> None: 52 | post = await post_service.delete_post(post_id) 53 | if not post: 54 | raise falcon.HTTPNotFound() 55 | 56 | 57 | @router.patch( 58 | "/posts/{post_id}", 59 | tags=["Posts"], 60 | response_errors=[400, 404, 422, 500], 61 | response_model=PostSchema, 62 | ) 63 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 64 | post = await post_service.update_post(post_id, body) 65 | if not post: 66 | raise falcon.HTTPNotFound() 67 | return post 68 | -------------------------------------------------------------------------------- /examples/falcon/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/falcon/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/falcon/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/falcon/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/falcon/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/falcon/app/services/__init__.py -------------------------------------------------------------------------------- /examples/falcon/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | async def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | async def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | async def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | async def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | async def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/falcon/app/services/posts.py: -------------------------------------------------------------------------------- 1 | import falcon 2 | 3 | from ..schemas.posts import ( 4 | CreatePostSchema, 5 | FilterPostSchema, 6 | PostSchema, 7 | UpdatePostSchema, 8 | ) 9 | from ..storage import authors, posts 10 | 11 | 12 | class PostService: 13 | async def create_post(self, body: CreatePostSchema) -> PostSchema: 14 | post_counter = len(posts) 15 | author = authors.get(body.author_id) 16 | if not author: 17 | raise falcon.HTTPNotFound(description="Author not found") 18 | 19 | data = { 20 | "id": post_counter, 21 | "title": body.title, 22 | "content": body.content, 23 | "author_id": body.author_id, 24 | } 25 | post = PostSchema(**data) 26 | posts[post_counter] = post 27 | return post 28 | 29 | async def get_post(self, post_id: int) -> PostSchema | None: 30 | return posts.get(post_id) 31 | 32 | async def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 33 | def match(post: PostSchema) -> bool: 34 | for field, value in body.model_dump(exclude_none=True).items(): 35 | if getattr(post, field) != value: 36 | return False 37 | return True 38 | 39 | return [item for _, item in posts.items() if match(item)] 40 | 41 | async def update_post( 42 | self, post_id: int, body: UpdatePostSchema 43 | ) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | async def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/falcon/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/falcon/run.py: -------------------------------------------------------------------------------- 1 | import falcon 2 | import uvicorn 3 | from app.api.routes import api_router 4 | 5 | from fastopenapi.routers import FalconRouter 6 | 7 | app = falcon.asgi.App() 8 | 9 | router = FalconRouter( 10 | app=app, 11 | title="MyFalconApp", 12 | version="0.0.1", 13 | docs_url="/docs", 14 | redoc_url="/redoc", 15 | openapi_version="3.0.0", 16 | ) 17 | router.include_router(api_router, prefix="/api") 18 | 19 | 20 | if __name__ == "__main__": 21 | uvicorn.run(app, host="127.0.0.1", port=8000) 22 | -------------------------------------------------------------------------------- /examples/falcon_example.py: -------------------------------------------------------------------------------- 1 | import falcon.asgi 2 | import uvicorn 3 | from pydantic import BaseModel 4 | 5 | from fastopenapi.routers import FalconRouter 6 | 7 | app = falcon.asgi.App() 8 | router = FalconRouter(app=app) 9 | 10 | 11 | class HelloResponse(BaseModel): 12 | message: str 13 | 14 | 15 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 16 | async def hello(name: str): 17 | """Say hello from Falcon""" 18 | return HelloResponse(message=f"Hello, {name}! It's Falcon!") 19 | 20 | 21 | if __name__ == "__main__": 22 | uvicorn.run(app, host="127.0.0.1", port=8000) 23 | -------------------------------------------------------------------------------- /examples/flask/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/flask/app/__init__.py -------------------------------------------------------------------------------- /examples/flask/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/flask/app/api/__init__.py -------------------------------------------------------------------------------- /examples/flask/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import FlaskRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = FlaskRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/flask/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import FlaskRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = FlaskRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/flask/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from flask import abort 4 | 5 | from fastopenapi.routers import FlaskRouter 6 | 7 | from ...schemas.authors import ( 8 | AuthorSchema, 9 | CreateAuthorSchema, 10 | FilterAuthorSchema, 11 | UpdateAuthorSchema, 12 | ) 13 | from ...services.authors import AuthorService 14 | 15 | author_service = AuthorService() 16 | 17 | router = FlaskRouter() 18 | 19 | 20 | @router.post( 21 | "/authors", 22 | tags=["Authors"], 23 | status_code=201, 24 | response_errors=[400, 422, 500], 25 | response_model=AuthorSchema, 26 | ) 27 | def create_author(body: CreateAuthorSchema) -> AuthorSchema: 28 | return author_service.create_author(body) 29 | 30 | 31 | @router.get( 32 | "/authors/{author_id}", 33 | tags=["Authors"], 34 | response_errors=[400, 404, 500], 35 | response_model=AuthorSchema, 36 | ) 37 | def get_author(author_id: int) -> AuthorSchema: 38 | author = author_service.get_author(author_id) 39 | if not author: 40 | abort(HTTPStatus.NOT_FOUND) 41 | return author 42 | 43 | 44 | @router.get( 45 | "/authors/", 46 | tags=["Authors"], 47 | response_errors=[500], 48 | response_model=list[AuthorSchema], 49 | ) 50 | def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 51 | return author_service.get_authors(body) 52 | 53 | 54 | @router.delete( 55 | "/authors/{author_id}", 56 | tags=["Authors"], 57 | status_code=204, 58 | response_errors=[400, 404, 500], 59 | ) 60 | def delete_author(author_id: int) -> None: 61 | author = author_service.delete_author(author_id) 62 | if not author: 63 | abort(HTTPStatus.NOT_FOUND) 64 | return None 65 | 66 | 67 | @router.patch( 68 | "/authors/{author_id}", 69 | tags=["Authors"], 70 | response_errors=[400, 404, 422, 500], 71 | response_model=AuthorSchema, 72 | ) 73 | def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 74 | author = author_service.update_author(author_id, body) 75 | if not author: 76 | abort(HTTPStatus.NOT_FOUND) 77 | return author 78 | -------------------------------------------------------------------------------- /examples/flask/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from flask import abort 4 | 5 | from fastopenapi.routers import FlaskRouter 6 | 7 | from ...schemas.posts import ( 8 | CreatePostSchema, 9 | FilterPostSchema, 10 | PostSchema, 11 | UpdatePostSchema, 12 | ) 13 | from ...services.posts import PostService 14 | 15 | post_service = PostService() 16 | router = FlaskRouter() 17 | 18 | 19 | @router.post( 20 | "/posts", 21 | tags=["Posts"], 22 | status_code=201, 23 | response_errors=[400, 422, 500], 24 | response_model=PostSchema, 25 | ) 26 | def create_post(body: CreatePostSchema) -> PostSchema: 27 | return post_service.create_post(body) 28 | 29 | 30 | @router.get( 31 | "/posts/{post_id}", 32 | tags=["Posts"], 33 | response_errors=[400, 404, 500], 34 | response_model=PostSchema, 35 | ) 36 | def get_post(post_id: int) -> PostSchema: 37 | post = post_service.get_post(post_id) 38 | if not post: 39 | abort(HTTPStatus.NOT_FOUND) 40 | return post 41 | 42 | 43 | @router.get( 44 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 45 | ) 46 | def get_posts(body: FilterPostSchema) -> list[PostSchema]: 47 | return post_service.get_posts(body) 48 | 49 | 50 | @router.delete( 51 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 52 | ) 53 | def delete_post(post_id: int) -> None: 54 | post = post_service.delete_post(post_id) 55 | if not post: 56 | abort(HTTPStatus.NOT_FOUND) 57 | 58 | 59 | @router.patch( 60 | "/posts/{post_id}", 61 | tags=["Posts"], 62 | response_errors=[400, 404, 422, 500], 63 | response_model=PostSchema, 64 | ) 65 | def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 66 | post = post_service.update_post(post_id, body) 67 | if not post: 68 | abort(HTTPStatus.NOT_FOUND) 69 | return post 70 | -------------------------------------------------------------------------------- /examples/flask/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/flask/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/flask/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/flask/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/flask/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/flask/app/services/__init__.py -------------------------------------------------------------------------------- /examples/flask/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/flask/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from flask import abort 4 | 5 | from ..schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ..storage import authors, posts 12 | 13 | 14 | class PostService: 15 | def create_post(self, body: CreatePostSchema) -> PostSchema: 16 | post_counter = len(posts) 17 | author = authors.get(body.author_id) 18 | if not author: 19 | abort(HTTPStatus.NOT_FOUND) 20 | 21 | data = { 22 | "id": post_counter, 23 | "title": body.title, 24 | "content": body.content, 25 | "author_id": body.author_id, 26 | } 27 | post = PostSchema(**data) 28 | posts[post_counter] = post 29 | return post 30 | 31 | def get_post(self, post_id: int) -> PostSchema | None: 32 | return posts.get(post_id) 33 | 34 | def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 35 | def match(post: PostSchema) -> bool: 36 | for field, value in body.model_dump(exclude_none=True).items(): 37 | if getattr(post, field) != value: 38 | return False 39 | return True 40 | 41 | return [item for _, item in posts.items() if match(item)] 42 | 43 | def update_post(self, post_id: int, body: UpdatePostSchema) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/flask/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/flask/run.py: -------------------------------------------------------------------------------- 1 | from app.api.routes import api_router 2 | from flask import Flask 3 | 4 | from fastopenapi.routers import FlaskRouter 5 | 6 | app = Flask(__name__) 7 | 8 | router = FlaskRouter( 9 | app=app, 10 | title="MyFlaskApp", 11 | version="0.0.1", 12 | docs_url="/docs", 13 | redoc_url="/redoc", 14 | openapi_version="3.0.0", 15 | ) 16 | router.include_router(api_router, prefix="/api") 17 | 18 | 19 | if __name__ == "__main__": 20 | app.run(debug=True, port=8000) 21 | -------------------------------------------------------------------------------- /examples/flask_example.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from pydantic import BaseModel 3 | 4 | from fastopenapi.routers import FlaskRouter 5 | 6 | app = Flask(__name__) 7 | router = FlaskRouter(app=app) 8 | 9 | 10 | class HelloResponse(BaseModel): 11 | message: str 12 | 13 | 14 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 15 | def hello(name: str): 16 | """Say hello from Flask""" 17 | return HelloResponse(message=f"Hello, {name}! It's Flask!") 18 | 19 | 20 | if __name__ == "__main__": 21 | app.run(port=8000) 22 | -------------------------------------------------------------------------------- /examples/quart/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/quart/app/__init__.py -------------------------------------------------------------------------------- /examples/quart/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/quart/app/api/__init__.py -------------------------------------------------------------------------------- /examples/quart/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import QuartRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = QuartRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/quart/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import QuartRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = QuartRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/quart/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from quart import abort 4 | 5 | from fastopenapi.routers import QuartRouter 6 | 7 | from ...schemas.authors import ( 8 | AuthorSchema, 9 | CreateAuthorSchema, 10 | FilterAuthorSchema, 11 | UpdateAuthorSchema, 12 | ) 13 | from ...services.authors import AuthorService 14 | 15 | author_service = AuthorService() 16 | 17 | router = QuartRouter() 18 | 19 | 20 | @router.post( 21 | "/authors", 22 | tags=["Authors"], 23 | status_code=201, 24 | response_errors=[400, 422, 500], 25 | response_model=AuthorSchema, 26 | ) 27 | async def create_author(body: CreateAuthorSchema) -> AuthorSchema: 28 | return author_service.create_author(body) 29 | 30 | 31 | @router.get( 32 | "/authors/{author_id}", 33 | tags=["Authors"], 34 | response_errors=[400, 404, 500], 35 | response_model=AuthorSchema, 36 | ) 37 | async def get_author(author_id: int) -> AuthorSchema: 38 | author = author_service.get_author(author_id) 39 | if not author: 40 | abort(HTTPStatus.NOT_FOUND) 41 | return author 42 | 43 | 44 | @router.get( 45 | "/authors/", 46 | tags=["Authors"], 47 | response_errors=[500], 48 | response_model=list[AuthorSchema], 49 | ) 50 | async def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 51 | return author_service.get_authors(body) 52 | 53 | 54 | @router.delete( 55 | "/authors/{author_id}", 56 | tags=["Authors"], 57 | status_code=204, 58 | response_errors=[400, 404, 500], 59 | ) 60 | async def delete_author(author_id: int) -> None: 61 | author = author_service.delete_author(author_id) 62 | if not author: 63 | abort(HTTPStatus.NOT_FOUND) 64 | return None 65 | 66 | 67 | @router.patch( 68 | "/authors/{author_id}", 69 | tags=["Authors"], 70 | response_errors=[400, 404, 422, 500], 71 | response_model=AuthorSchema, 72 | ) 73 | async def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 74 | author = author_service.update_author(author_id, body) 75 | if not author: 76 | abort(HTTPStatus.NOT_FOUND) 77 | return author 78 | -------------------------------------------------------------------------------- /examples/quart/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from quart import abort 4 | 5 | from fastopenapi.routers import QuartRouter 6 | 7 | from ...schemas.posts import ( 8 | CreatePostSchema, 9 | FilterPostSchema, 10 | PostSchema, 11 | UpdatePostSchema, 12 | ) 13 | from ...services.posts import PostService 14 | 15 | post_service = PostService() 16 | router = QuartRouter() 17 | 18 | 19 | @router.post( 20 | "/posts", 21 | tags=["Posts"], 22 | status_code=201, 23 | response_errors=[400, 422, 500], 24 | response_model=PostSchema, 25 | ) 26 | async def create_post(body: CreatePostSchema) -> PostSchema: 27 | return post_service.create_post(body) 28 | 29 | 30 | @router.get( 31 | "/posts/{post_id}", 32 | tags=["Posts"], 33 | response_errors=[400, 404, 500], 34 | response_model=PostSchema, 35 | ) 36 | async def get_post(post_id: int) -> PostSchema: 37 | post = post_service.get_post(post_id) 38 | if not post: 39 | abort(HTTPStatus.NOT_FOUND) 40 | return post 41 | 42 | 43 | @router.get( 44 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 45 | ) 46 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 47 | return post_service.get_posts(body) 48 | 49 | 50 | @router.delete( 51 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 52 | ) 53 | async def delete_post(post_id: int) -> None: 54 | post = post_service.delete_post(post_id) 55 | if not post: 56 | abort(HTTPStatus.NOT_FOUND) 57 | 58 | 59 | @router.patch( 60 | "/posts/{post_id}", 61 | tags=["Posts"], 62 | response_errors=[400, 404, 422, 500], 63 | response_model=PostSchema, 64 | ) 65 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 66 | post = post_service.update_post(post_id, body) 67 | if not post: 68 | abort(HTTPStatus.NOT_FOUND) 69 | return post 70 | -------------------------------------------------------------------------------- /examples/quart/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/quart/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/quart/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/quart/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/quart/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/quart/app/services/__init__.py -------------------------------------------------------------------------------- /examples/quart/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/quart/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | 3 | from quart import abort 4 | 5 | from ..schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ..storage import authors, posts 12 | 13 | 14 | class PostService: 15 | def create_post(self, body: CreatePostSchema) -> PostSchema: 16 | post_counter = len(posts) 17 | author = authors.get(body.author_id) 18 | if not author: 19 | abort(HTTPStatus.NOT_FOUND) 20 | 21 | data = { 22 | "id": post_counter, 23 | "title": body.title, 24 | "content": body.content, 25 | "author_id": body.author_id, 26 | } 27 | post = PostSchema(**data) 28 | posts[post_counter] = post 29 | return post 30 | 31 | def get_post(self, post_id: int) -> PostSchema | None: 32 | return posts.get(post_id) 33 | 34 | def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 35 | def match(post: PostSchema) -> bool: 36 | for field, value in body.model_dump(exclude_none=True).items(): 37 | if getattr(post, field) != value: 38 | return False 39 | return True 40 | 41 | return [item for _, item in posts.items() if match(item)] 42 | 43 | def update_post(self, post_id: int, body: UpdatePostSchema) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/quart/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/quart/run.py: -------------------------------------------------------------------------------- 1 | from app.api.routes import api_router 2 | from quart import Quart 3 | 4 | from fastopenapi.routers import QuartRouter 5 | 6 | app = Quart(__name__) 7 | 8 | router = QuartRouter( 9 | app=app, 10 | title="MyQuartApp", 11 | version="0.0.1", 12 | docs_url="/docs", 13 | redoc_url="/redoc", 14 | openapi_version="3.0.0", 15 | ) 16 | router.include_router(api_router, prefix="/api") 17 | 18 | 19 | if __name__ == "__main__": 20 | app.run(debug=True, port=8000) 21 | -------------------------------------------------------------------------------- /examples/quart_example.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from quart import Quart 3 | 4 | from fastopenapi.routers import QuartRouter 5 | 6 | app = Quart(__name__) 7 | router = QuartRouter(app=app) 8 | 9 | 10 | class HelloResponse(BaseModel): 11 | message: str 12 | 13 | 14 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 15 | async def hello(name: str): 16 | """Say hello from Quart""" 17 | return HelloResponse(message=f"Hello, {name}! It's Quart!") 18 | 19 | 20 | if __name__ == "__main__": 21 | app.run(port=8000) 22 | -------------------------------------------------------------------------------- /examples/sanic/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/sanic/app/__init__.py -------------------------------------------------------------------------------- /examples/sanic/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/sanic/app/api/__init__.py -------------------------------------------------------------------------------- /examples/sanic/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import SanicRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = SanicRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/sanic/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import SanicRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = SanicRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/sanic/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | from sanic import NotFound 2 | 3 | from fastopenapi.routers import SanicRouter 4 | 5 | from ...schemas.authors import ( 6 | AuthorSchema, 7 | CreateAuthorSchema, 8 | FilterAuthorSchema, 9 | UpdateAuthorSchema, 10 | ) 11 | from ...services.authors import AuthorService 12 | 13 | author_service = AuthorService() 14 | 15 | router = SanicRouter() 16 | 17 | 18 | @router.post( 19 | "/authors", 20 | tags=["Authors"], 21 | status_code=201, 22 | response_errors=[400, 422, 500], 23 | response_model=AuthorSchema, 24 | ) 25 | async def create_author(body: CreateAuthorSchema) -> AuthorSchema: 26 | return await author_service.create_author(body) 27 | 28 | 29 | @router.get( 30 | "/authors/{author_id}", 31 | tags=["Authors"], 32 | response_errors=[400, 404, 500], 33 | response_model=AuthorSchema, 34 | ) 35 | async def get_author(author_id: int) -> AuthorSchema: 36 | author = await author_service.get_author(author_id) 37 | if not author: 38 | raise NotFound() 39 | return author 40 | 41 | 42 | @router.get( 43 | "/authors/", 44 | tags=["Authors"], 45 | response_errors=[500], 46 | response_model=list[AuthorSchema], 47 | ) 48 | async def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 49 | return await author_service.get_authors(body) 50 | 51 | 52 | @router.delete( 53 | "/authors/{author_id}", 54 | tags=["Authors"], 55 | status_code=204, 56 | response_errors=[400, 404, 500], 57 | ) 58 | async def delete_author(author_id: int) -> None: 59 | author = await author_service.delete_author(author_id) 60 | if not author: 61 | raise NotFound() 62 | 63 | 64 | @router.patch( 65 | "/authors/{author_id}", 66 | tags=["Authors"], 67 | response_errors=[400, 404, 422, 500], 68 | response_model=AuthorSchema, 69 | ) 70 | async def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 71 | author = await author_service.update_author(author_id, body) 72 | if not author: 73 | raise NotFound() 74 | return author 75 | -------------------------------------------------------------------------------- /examples/sanic/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from sanic import NotFound 2 | 3 | from fastopenapi.routers import SanicRouter 4 | 5 | from ...schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ...services.posts import PostService 12 | 13 | post_service = PostService() 14 | router = SanicRouter() 15 | 16 | 17 | @router.post( 18 | "/posts", 19 | tags=["Posts"], 20 | status_code=201, 21 | response_errors=[400, 422, 500], 22 | response_model=PostSchema, 23 | ) 24 | async def create_post(body: CreatePostSchema) -> PostSchema: 25 | return await post_service.create_post(body) 26 | 27 | 28 | @router.get( 29 | "/posts/{post_id}", 30 | tags=["Posts"], 31 | response_errors=[400, 404, 500], 32 | response_model=PostSchema, 33 | ) 34 | async def get_post(post_id: int) -> PostSchema: 35 | post = await post_service.get_post(post_id) 36 | if not post: 37 | raise NotFound() 38 | return post 39 | 40 | 41 | @router.get( 42 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 43 | ) 44 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 45 | return await post_service.get_posts(body) 46 | 47 | 48 | @router.delete( 49 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 50 | ) 51 | async def delete_post(post_id: int) -> None: 52 | post = await post_service.delete_post(post_id) 53 | if not post: 54 | raise NotFound() 55 | 56 | 57 | @router.patch( 58 | "/posts/{post_id}", 59 | tags=["Posts"], 60 | response_errors=[400, 404, 422, 500], 61 | response_model=PostSchema, 62 | ) 63 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 64 | post = await post_service.update_post(post_id, body) 65 | if not post: 66 | raise NotFound() 67 | return post 68 | -------------------------------------------------------------------------------- /examples/sanic/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/sanic/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/sanic/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/sanic/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/sanic/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/sanic/app/services/__init__.py -------------------------------------------------------------------------------- /examples/sanic/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | async def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | async def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | async def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | async def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | async def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/sanic/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from sanic import NotFound 2 | 3 | from ..schemas.posts import ( 4 | CreatePostSchema, 5 | FilterPostSchema, 6 | PostSchema, 7 | UpdatePostSchema, 8 | ) 9 | from ..storage import authors, posts 10 | 11 | 12 | class PostService: 13 | async def create_post(self, body: CreatePostSchema) -> PostSchema: 14 | post_counter = len(posts) 15 | author = authors.get(body.author_id) 16 | if not author: 17 | raise NotFound("Author not found") 18 | 19 | data = { 20 | "id": post_counter, 21 | "title": body.title, 22 | "content": body.content, 23 | "author_id": body.author_id, 24 | } 25 | post = PostSchema(**data) 26 | posts[post_counter] = post 27 | return post 28 | 29 | async def get_post(self, post_id: int) -> PostSchema | None: 30 | return posts.get(post_id) 31 | 32 | async def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 33 | def match(post: PostSchema) -> bool: 34 | for field, value in body.model_dump(exclude_none=True).items(): 35 | if getattr(post, field) != value: 36 | return False 37 | return True 38 | 39 | return [item for _, item in posts.items() if match(item)] 40 | 41 | async def update_post( 42 | self, post_id: int, body: UpdatePostSchema 43 | ) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | async def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/sanic/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/sanic/run.py: -------------------------------------------------------------------------------- 1 | from app.api.routes import api_router 2 | from sanic import Sanic 3 | 4 | from fastopenapi.routers import SanicRouter 5 | 6 | app = Sanic("MySanicApp") 7 | 8 | router = SanicRouter( 9 | app=app, 10 | title="MySanicApp", 11 | version="0.0.1", 12 | docs_url="/docs", 13 | redoc_url="/redoc", 14 | openapi_version="3.0.0", 15 | ) 16 | router.include_router(api_router, prefix="/api") 17 | 18 | 19 | if __name__ == "__main__": 20 | app.run(host="0.0.0.0", port=8000) 21 | -------------------------------------------------------------------------------- /examples/sanic_example.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from sanic import Sanic 3 | 4 | from fastopenapi.routers import SanicRouter 5 | 6 | app = Sanic("MySanicApp") 7 | router = SanicRouter(app=app) 8 | 9 | 10 | class HelloResponse(BaseModel): 11 | message: str 12 | 13 | 14 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 15 | async def hello(name: str): 16 | """Say hello from Sanic""" 17 | return HelloResponse(message=f"Hello, {name}! It's Sanic!") 18 | 19 | 20 | if __name__ == "__main__": 21 | app.run(host="0.0.0.0", port=8000) 22 | -------------------------------------------------------------------------------- /examples/starlette/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/starlette/app/__init__.py -------------------------------------------------------------------------------- /examples/starlette/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/starlette/app/api/__init__.py -------------------------------------------------------------------------------- /examples/starlette/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import StarletteRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = StarletteRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/starlette/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import StarletteRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = StarletteRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/starlette/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from starlette.exceptions import HTTPException 2 | 3 | from fastopenapi.routers import StarletteRouter 4 | 5 | from ...schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ...services.posts import PostService 12 | 13 | post_service = PostService() 14 | router = StarletteRouter() 15 | 16 | 17 | @router.post( 18 | "/posts", 19 | tags=["Posts"], 20 | status_code=201, 21 | response_errors=[400, 422, 500], 22 | response_model=PostSchema, 23 | ) 24 | async def create_post(body: CreatePostSchema) -> PostSchema: 25 | return await post_service.create_post(body) 26 | 27 | 28 | @router.get( 29 | "/posts/{post_id}", 30 | tags=["Posts"], 31 | response_errors=[400, 404, 500], 32 | response_model=PostSchema, 33 | ) 34 | async def get_post(post_id: int) -> PostSchema: 35 | post = await post_service.get_post(post_id) 36 | if not post: 37 | raise HTTPException(status_code=404, detail="Not Found") 38 | return post 39 | 40 | 41 | @router.get( 42 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 43 | ) 44 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 45 | return await post_service.get_posts(body) 46 | 47 | 48 | @router.delete( 49 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 50 | ) 51 | async def delete_post(post_id: int) -> None: 52 | post = await post_service.delete_post(post_id) 53 | if not post: 54 | raise HTTPException(status_code=404, detail="Not Found") 55 | 56 | 57 | @router.patch( 58 | "/posts/{post_id}", 59 | tags=["Posts"], 60 | response_errors=[400, 404, 422, 500], 61 | response_model=PostSchema, 62 | ) 63 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 64 | post = await post_service.update_post(post_id, body) 65 | if not post: 66 | raise HTTPException(status_code=404, detail="Not Found") 67 | return post 68 | -------------------------------------------------------------------------------- /examples/starlette/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/starlette/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/starlette/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/starlette/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/starlette/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/starlette/app/services/__init__.py -------------------------------------------------------------------------------- /examples/starlette/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | async def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | async def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | async def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | async def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | async def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/starlette/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from starlette.exceptions import HTTPException 2 | 3 | from ..schemas.posts import ( 4 | CreatePostSchema, 5 | FilterPostSchema, 6 | PostSchema, 7 | UpdatePostSchema, 8 | ) 9 | from ..storage import authors, posts 10 | 11 | 12 | class PostService: 13 | async def create_post(self, body: CreatePostSchema) -> PostSchema: 14 | post_counter = len(posts) 15 | author = authors.get(body.author_id) 16 | if not author: 17 | raise HTTPException(status_code=404, detail="Author not found") 18 | 19 | data = { 20 | "id": post_counter, 21 | "title": body.title, 22 | "content": body.content, 23 | "author_id": body.author_id, 24 | } 25 | post = PostSchema(**data) 26 | posts[post_counter] = post 27 | return post 28 | 29 | async def get_post(self, post_id: int) -> PostSchema | None: 30 | return posts.get(post_id) 31 | 32 | async def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 33 | def match(post: PostSchema) -> bool: 34 | for field, value in body.model_dump(exclude_none=True).items(): 35 | if getattr(post, field) != value: 36 | return False 37 | return True 38 | 39 | return [item for _, item in posts.items() if match(item)] 40 | 41 | async def update_post( 42 | self, post_id: int, body: UpdatePostSchema 43 | ) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | async def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/starlette/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/starlette/run.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | from app.api.routes import api_router 3 | from starlette.applications import Starlette 4 | 5 | from fastopenapi.routers import StarletteRouter 6 | 7 | app = Starlette() 8 | 9 | router = StarletteRouter( 10 | app=app, 11 | title="MyStarletteApp", 12 | version="0.0.1", 13 | docs_url="/docs", 14 | redoc_url="/redoc", 15 | openapi_version="3.0.0", 16 | ) 17 | router.include_router(api_router, prefix="/api") 18 | 19 | 20 | if __name__ == "__main__": 21 | uvicorn.run(app, host="127.0.0.1", port=8000) 22 | -------------------------------------------------------------------------------- /examples/starlette_example.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | from pydantic import BaseModel 3 | from starlette.applications import Starlette 4 | 5 | from fastopenapi.routers import StarletteRouter 6 | 7 | app = Starlette() 8 | router = StarletteRouter(app=app) 9 | 10 | 11 | class HelloResponse(BaseModel): 12 | message: str 13 | 14 | 15 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 16 | async def hello(name: str): 17 | """Say hello from Starlette""" 18 | return HelloResponse(message=f"Hello, {name}! It's Starlette!") 19 | 20 | 21 | if __name__ == "__main__": 22 | uvicorn.run(app, host="127.0.0.1", port=8000) 23 | -------------------------------------------------------------------------------- /examples/tornado/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/tornado/app/__init__.py -------------------------------------------------------------------------------- /examples/tornado/app/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/tornado/app/api/__init__.py -------------------------------------------------------------------------------- /examples/tornado/app/api/routes.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import TornadoRouter 2 | 3 | from .v1 import v1_router 4 | 5 | api_router = TornadoRouter() 6 | api_router.include_router(v1_router, prefix="/v1") 7 | -------------------------------------------------------------------------------- /examples/tornado/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastopenapi.routers import TornadoRouter 2 | 3 | from .authors import router as authors_router 4 | from .posts import router as posts_router 5 | 6 | v1_router = TornadoRouter() 7 | v1_router.include_router(authors_router) 8 | v1_router.include_router(posts_router) 9 | -------------------------------------------------------------------------------- /examples/tornado/app/api/v1/authors.py: -------------------------------------------------------------------------------- 1 | from tornado.web import HTTPError 2 | 3 | from fastopenapi.routers import TornadoRouter 4 | 5 | from ...schemas.authors import ( 6 | AuthorSchema, 7 | CreateAuthorSchema, 8 | FilterAuthorSchema, 9 | UpdateAuthorSchema, 10 | ) 11 | from ...services.authors import AuthorService 12 | 13 | author_service = AuthorService() 14 | 15 | router = TornadoRouter() 16 | 17 | 18 | @router.post( 19 | "/authors", 20 | tags=["Authors"], 21 | status_code=201, 22 | response_errors=[400, 422, 500], 23 | response_model=AuthorSchema, 24 | ) 25 | async def create_author(body: CreateAuthorSchema) -> AuthorSchema: 26 | return await author_service.create_author(body) 27 | 28 | 29 | @router.get( 30 | "/authors/{author_id}", 31 | tags=["Authors"], 32 | response_errors=[400, 404, 500], 33 | response_model=AuthorSchema, 34 | ) 35 | async def get_author(author_id: int) -> AuthorSchema: 36 | author = await author_service.get_author(author_id) 37 | if not author: 38 | raise HTTPError(status_code=404, reason="Not Found") 39 | return author 40 | 41 | 42 | @router.get( 43 | "/authors/", 44 | tags=["Authors"], 45 | response_errors=[500], 46 | response_model=list[AuthorSchema], 47 | ) 48 | async def get_authors(body: FilterAuthorSchema) -> list[AuthorSchema]: 49 | return await author_service.get_authors(body) 50 | 51 | 52 | @router.delete( 53 | "/authors/{author_id}", 54 | tags=["Authors"], 55 | status_code=204, 56 | response_errors=[400, 404, 500], 57 | ) 58 | async def delete_author(author_id: int) -> None: 59 | author = await author_service.delete_author(author_id) 60 | if not author: 61 | raise HTTPError(status_code=404, reason="Not Found") 62 | 63 | 64 | @router.patch( 65 | "/authors/{author_id}", 66 | tags=["Authors"], 67 | response_errors=[400, 404, 422, 500], 68 | response_model=AuthorSchema, 69 | ) 70 | async def update_author(author_id: int, body: UpdateAuthorSchema) -> AuthorSchema: 71 | author = await author_service.update_author(author_id, body) 72 | if not author: 73 | raise HTTPError(status_code=404, reason="Not Found") 74 | return author 75 | -------------------------------------------------------------------------------- /examples/tornado/app/api/v1/posts.py: -------------------------------------------------------------------------------- 1 | from tornado.web import HTTPError 2 | 3 | from fastopenapi.routers import TornadoRouter 4 | 5 | from ...schemas.posts import ( 6 | CreatePostSchema, 7 | FilterPostSchema, 8 | PostSchema, 9 | UpdatePostSchema, 10 | ) 11 | from ...services.posts import PostService 12 | 13 | post_service = PostService() 14 | router = TornadoRouter() 15 | 16 | 17 | @router.post( 18 | "/posts", 19 | tags=["Posts"], 20 | status_code=201, 21 | response_errors=[400, 422, 500], 22 | response_model=PostSchema, 23 | ) 24 | async def create_post(body: CreatePostSchema) -> PostSchema: 25 | return await post_service.create_post(body) 26 | 27 | 28 | @router.get( 29 | "/posts/{post_id}", 30 | tags=["Posts"], 31 | response_errors=[400, 404, 500], 32 | response_model=PostSchema, 33 | ) 34 | async def get_post(post_id: int) -> PostSchema: 35 | post = await post_service.get_post(post_id) 36 | if not post: 37 | raise HTTPError(status_code=404, reason="Not Found") 38 | return post 39 | 40 | 41 | @router.get( 42 | "/posts/", tags=["Posts"], response_errors=[500], response_model=list[PostSchema] 43 | ) 44 | async def get_posts(body: FilterPostSchema) -> list[PostSchema]: 45 | return await post_service.get_posts(body) 46 | 47 | 48 | @router.delete( 49 | "/posts/{post_id}", tags=["Posts"], status_code=204, response_errors=[400, 404, 500] 50 | ) 51 | async def delete_post(post_id: int) -> None: 52 | post = await post_service.delete_post(post_id) 53 | if not post: 54 | raise HTTPError(status_code=404, reason="Not Found") 55 | 56 | 57 | @router.patch( 58 | "/posts/{post_id}", 59 | tags=["Posts"], 60 | response_errors=[400, 404, 422, 500], 61 | response_model=PostSchema, 62 | ) 63 | async def update_post(post_id: int, body: UpdatePostSchema) -> PostSchema: 64 | post = await post_service.update_post(post_id, body) 65 | if not post: 66 | raise HTTPError(status_code=404, reason="Not Found") 67 | return post 68 | -------------------------------------------------------------------------------- /examples/tornado/app/schemas/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/tornado/app/schemas/__init__.py -------------------------------------------------------------------------------- /examples/tornado/app/schemas/authors.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class AuthorSchema(BaseModel): 5 | id: int 6 | name: str 7 | bio: str | None = None 8 | 9 | 10 | class FilterAuthorSchema(BaseModel): 11 | id: int = Field(default=None) 12 | name: str = Field(default=None) 13 | 14 | 15 | class CreateAuthorSchema(BaseModel): 16 | name: str = Field(..., max_length=50) 17 | bio: str | None = Field(None, max_length=200) 18 | 19 | 20 | class UpdateAuthorSchema(BaseModel): 21 | name: str = Field(default=None, max_length=50) 22 | bio: str = Field(default=None, max_length=200) 23 | -------------------------------------------------------------------------------- /examples/tornado/app/schemas/posts.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class PostSchema(BaseModel): 5 | id: int 6 | title: str 7 | content: str 8 | author_id: int 9 | 10 | 11 | class FilterPostSchema(BaseModel): 12 | id: int = Field(default=None) 13 | title: str = Field(default=None) 14 | 15 | 16 | class CreatePostSchema(BaseModel): 17 | title: str = Field(..., max_length=100) 18 | content: str 19 | author_id: int 20 | 21 | 22 | class UpdatePostSchema(BaseModel): 23 | title: str = Field(default=None, max_length=100) 24 | content: str = Field(default=None) 25 | -------------------------------------------------------------------------------- /examples/tornado/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/examples/tornado/app/services/__init__.py -------------------------------------------------------------------------------- /examples/tornado/app/services/authors.py: -------------------------------------------------------------------------------- 1 | from ..schemas.authors import ( 2 | AuthorSchema, 3 | CreateAuthorSchema, 4 | FilterAuthorSchema, 5 | UpdateAuthorSchema, 6 | ) 7 | from ..storage import authors, posts 8 | 9 | 10 | class AuthorService: 11 | async def create_author(self, body: CreateAuthorSchema) -> AuthorSchema: 12 | author_counter = len(authors) 13 | data = {"id": author_counter, "name": body.name, "bio": body.bio} 14 | author = AuthorSchema(**data) 15 | authors[author_counter] = author 16 | return author 17 | 18 | async def get_author(self, author_id: int) -> AuthorSchema | None: 19 | return authors.get(author_id) 20 | 21 | async def get_authors(self, body: FilterAuthorSchema) -> list[AuthorSchema]: 22 | def match(author: AuthorSchema) -> bool: 23 | for field, value in body.model_dump(exclude_none=True).items(): 24 | if getattr(author, field) != value: 25 | return False 26 | return True 27 | 28 | return [item for _, item in authors.items() if match(item)] 29 | 30 | async def update_author( 31 | self, author_id: int, body: UpdateAuthorSchema 32 | ) -> AuthorSchema | None: 33 | author = authors.get(author_id) 34 | if author: 35 | updated_author = author.copy(update=body.model_dump(exclude_unset=True)) 36 | authors[author_id] = updated_author 37 | return updated_author 38 | return None 39 | 40 | async def delete_author(self, author_id: int) -> int | None: 41 | author_id = authors.pop(author_id, None) 42 | if author_id: 43 | keys_to_remove = [ 44 | post_id 45 | for post_id, post in posts.items() 46 | if post.author_id == author_id.id 47 | ] 48 | for key in keys_to_remove: 49 | del posts[key] 50 | return author_id 51 | -------------------------------------------------------------------------------- /examples/tornado/app/services/posts.py: -------------------------------------------------------------------------------- 1 | from tornado.web import HTTPError 2 | 3 | from ..schemas.posts import ( 4 | CreatePostSchema, 5 | FilterPostSchema, 6 | PostSchema, 7 | UpdatePostSchema, 8 | ) 9 | from ..storage import authors, posts 10 | 11 | 12 | class PostService: 13 | async def create_post(self, body: CreatePostSchema) -> PostSchema: 14 | post_counter = len(posts) 15 | author = authors.get(body.author_id) 16 | if not author: 17 | raise HTTPError(status_code=404, log_message="Author not Found") 18 | 19 | data = { 20 | "id": post_counter, 21 | "title": body.title, 22 | "content": body.content, 23 | "author_id": body.author_id, 24 | } 25 | post = PostSchema(**data) 26 | posts[post_counter] = post 27 | return post 28 | 29 | async def get_post(self, post_id: int) -> PostSchema | None: 30 | return posts.get(post_id) 31 | 32 | async def get_posts(self, body: FilterPostSchema) -> list[PostSchema]: 33 | def match(post: PostSchema) -> bool: 34 | for field, value in body.model_dump(exclude_none=True).items(): 35 | if getattr(post, field) != value: 36 | return False 37 | return True 38 | 39 | return [item for _, item in posts.items() if match(item)] 40 | 41 | async def update_post( 42 | self, post_id: int, body: UpdatePostSchema 43 | ) -> PostSchema | None: 44 | post = posts.get(post_id) 45 | if post: 46 | updated_post = post.copy(update=body.model_dump(exclude_unset=True)) 47 | posts[post_id] = updated_post 48 | return updated_post 49 | return None 50 | 51 | async def delete_post(self, post_id: int) -> int | None: 52 | return posts.pop(post_id, None) 53 | -------------------------------------------------------------------------------- /examples/tornado/app/storage.py: -------------------------------------------------------------------------------- 1 | authors = {} 2 | posts = {} 3 | -------------------------------------------------------------------------------- /examples/tornado/run.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from app.api.routes import api_router 4 | from tornado.web import Application 5 | 6 | from fastopenapi.routers import TornadoRouter 7 | 8 | app = Application() 9 | 10 | router = TornadoRouter( 11 | app=app, 12 | title="MyTornadoApp", 13 | version="0.0.1", 14 | docs_url="/docs", 15 | redoc_url="/redoc", 16 | openapi_version="3.0.0", 17 | ) 18 | router.include_router(api_router, prefix="/api") 19 | 20 | 21 | async def main(): 22 | app.listen(8000) 23 | await asyncio.Event().wait() 24 | 25 | 26 | if __name__ == "__main__": 27 | asyncio.run(main()) 28 | -------------------------------------------------------------------------------- /examples/tornado_example.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from pydantic import BaseModel 4 | from tornado.web import Application 5 | 6 | from fastopenapi.routers.tornado import TornadoRouter 7 | 8 | app = Application() 9 | 10 | router = TornadoRouter(app=app) 11 | 12 | 13 | class HelloResponse(BaseModel): 14 | message: str 15 | 16 | 17 | @router.get("/hello", tags=["Hello"], status_code=200, response_model=HelloResponse) 18 | def hello(name: str): 19 | """Say hello from Tornado""" 20 | return HelloResponse(message=f"Hello, {name}! It's Tornado!") 21 | 22 | 23 | async def main(): 24 | app.listen(8000) 25 | await asyncio.Event().wait() 26 | 27 | 28 | if __name__ == "__main__": 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /fastopenapi/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.7.0" 2 | -------------------------------------------------------------------------------- /fastopenapi/routers/__init__.py: -------------------------------------------------------------------------------- 1 | class MissingRouter: 2 | def __init__(self, *args, **kwargs): 3 | raise ImportError("This framework is not installed.") 4 | 5 | 6 | try: 7 | from fastopenapi.routers.aiohttp import AioHttpRouter 8 | except ModuleNotFoundError: 9 | AioHttpRouter = MissingRouter 10 | 11 | try: 12 | from fastopenapi.routers.falcon import FalconRouter 13 | except ModuleNotFoundError: 14 | FalconRouter = MissingRouter 15 | 16 | try: 17 | from fastopenapi.routers.flask import FlaskRouter 18 | except ModuleNotFoundError: 19 | FlaskRouter = MissingRouter 20 | 21 | try: 22 | from fastopenapi.routers.quart import QuartRouter 23 | except ModuleNotFoundError: 24 | QuartRouter = MissingRouter 25 | 26 | try: 27 | from fastopenapi.routers.sanic import SanicRouter 28 | except ModuleNotFoundError: 29 | SanicRouter = MissingRouter 30 | 31 | try: 32 | from fastopenapi.routers.starlette import StarletteRouter 33 | except ModuleNotFoundError: 34 | StarletteRouter = MissingRouter 35 | 36 | try: 37 | from fastopenapi.routers.tornado import TornadoRouter 38 | except ModuleNotFoundError: 39 | TornadoRouter = MissingRouter 40 | 41 | __all__ = [ 42 | "AioHttpRouter", 43 | "FalconRouter", 44 | "FlaskRouter", 45 | "QuartRouter", 46 | "SanicRouter", 47 | "StarletteRouter", 48 | "TornadoRouter", 49 | ] 50 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/logo.png -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | asyncio_default_fixture_loop_scope = function 3 | 4 | markers = 5 | asyncio: mark test as asyncio 6 | 7 | filterwarnings = 8 | ignore::Warning:.* 9 | default::Warning:fastopenapi\..* 10 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # TODO rework all tests later 2 | -------------------------------------------------------------------------------- /tests/aiohttp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/aiohttp/__init__.py -------------------------------------------------------------------------------- /tests/falcon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/falcon/__init__.py -------------------------------------------------------------------------------- /tests/flask/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/flask/__init__.py -------------------------------------------------------------------------------- /tests/quart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/quart/__init__.py -------------------------------------------------------------------------------- /tests/sanic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/sanic/__init__.py -------------------------------------------------------------------------------- /tests/starlette/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/starlette/__init__.py -------------------------------------------------------------------------------- /tests/tornado/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-fatalyst/fastopenapi/e6a94e8e8c22d057242bde394e15b0f7f6276f5d/tests/tornado/__init__.py --------------------------------------------------------------------------------