I initiated a Flask app (+ Connexion and Swagger UI) and tried to open http://127.0.0.1:5000/api/ui. The browser showed starlette.exceptions.HTTPException: 404: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
% pip install "connexion[flask, swagger-ui]"
% export FLASK_APP="app"
(Prepare files)
% flask run --debug
(Access http://127.0.0.1:5000/api/ui)
Result
Directory structure
app/
__init__.py
openapi.yaml
hello.py
__init__.py
from connexion import FlaskApp
from flask.app import Flask
from pathlib import Path
BASE_DIR = Path(__file__).parent.resolve()
def create_app() -> Flask:
flask_app: FlaskApp = FlaskApp(__name__)
app: Flask = flask_app.app
flask_app.add_api("openapi.yaml")
return app
openapi.yaml
openapi: 3.0.3
info:
title: "test"
description: "test"
version: "1.0.0"
servers:
- url: "/api"
paths:
/hello:
get:
summary: "hello"
description: "hello"
operationId: "hello.say_hello"
responses:
200:
description: "OK"
content:
text/plain:
schema:
type: string
example: "hello"
hello.py
def say_hello() -> str:
return 'Hello, world!'
Based on these settings, I believe I can see Swagger UI at http://127.0.0.1:5000/api/ui. However, I faced the error message below.
Traceback (most recent call last):
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 867, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 841, in dispatch_request
self.raise_routing_exception(req)
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 450, in raise_routing_exception
raise request.routing_exception # type: ignore
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/ctx.py", line 353, in match_request
result = self.url_adapter.match(return_rule=True) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/werkzeug/routing/map.py", line 624, in match
raise NotFound() from None
werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 1478, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 1458, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 1455, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 869, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/flask/app.py", line 759, in handle_user_exception
return self.ensure_sync(handler)(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/myusername/tmp/.venv/lib/python3.12/site-packages/connexion/apps/flask.py", line 245, in _http_exception
raise starlette.exceptions.HTTPException(exc.code, detail=exc.description)
starlette.exceptions.HTTPException: 404: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
TL:DR: You need an ASGI server to run your application.
A similar problem on the connexion github issue tracker.
This will only lead you to the documentation on running your application which can be found here.
Bearing the above in mind, I managed to create my own solution:
__init__.py
:
from connexion import FlaskApp
def create_app():
app = FlaskApp(__name__)
app.add_api("openapi.yaml", validate_responses=True)
return app
app = create_app()
openapi.yaml
:
openapi: 3.0.3
info:
title: test
description: test
version: 1.0.0
paths:
/hello:
get:
summary: hello
description: hello
operationId: app.hello.say_hello
responses:
200:
description: OK
content:
text/plain:
schema:
type: string
example: hello
For additional assistance, please see below the docker setup I used:
docker-compose.yaml
:
version: '3.8'
services:
test-api:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
env_file:
- .env
ports:
- '8000:8000'
volumes:
- ./app:/usr/src/app/app
Dockerfile
:
FROM public.ecr.aws/lambda/python:3.10
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip3 install --no-cache-dir -r requirements.txt
ENV FLASK_RUN_HOST=0.0.0.0
ENV FLASK_RUN_PORT=8000
EXPOSE 8000
COPY entrypoint.sh /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
entrypoint.sh
:
#!/bin/sh
gunicorn -w 1 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 app:app --reload
# Keep the script running to keep the container alive
tail -f /dev/null
requirements.txt
:
connexion[flask, swagger-ui, uvicorn]==3.0.2
gunicorn==21.2.0
Werkzeug==3.0.1
Flask==3.0.0
setuptools >= 21.0.0
swagger-ui-bundle==1.1.0
project structure:
project-root/
|-- app/
| |-- __init__.py
| |-- hello.py
| |-- openapi.yaml
|-- Dockerfile
|-- docker-compose.yml
|-- entrypoint.sh
|-- requirements.txt
|-- .env
Hope this helps! I'd recommend playing around from this working point with specific settings such as CORS, db setup, migrations, alterations to the docker setup etc.