connexion Documentation

Connexion is a modern Python web framework that makes spec-first and api-first development easy.

RAW Doc

File: README.md


Connexion is a modern Python web framework that makes spec-first and api-first development easy.
You describe your API in an OpenAPI (or Swagger) specification with as much
detail as you want and Connexion will guarantee that it works as you specified.

It works either standalone, or in combination with any ASGI or WSGI-compatible framework!

โœจ Features

Connexion provides the following functionality based on your specification:

  • ๐Ÿš Automatic route registration, no @route decorators needed
  • ๐Ÿ”’ Authentication, split from your application logic
  • ๐Ÿ”Ž Request and response validation of headers, parameters, and body
  • ๐Ÿ“ฌ Parameter parsing and injection, no request object needed
  • ๐Ÿ“จ Response serialization, you can return regular Python objects
  • ๐Ÿ“บ A Swagger UI console with live documentation and โ€˜try it outโ€™ feature
  • ๐Ÿงฉ Pluggability, in all dimensions

Connexion also helps you write your OpenAPI specification and develop against it by providing a command line interface which lets you test and mock your specification.

shell
connexion run openapi.yaml

(back to top)

๐Ÿซถ Sponsors

Sponsors help us dedicate time to maintain Connexion. Want to help?

Explore the options ยป

(back to top)

๐Ÿชค Why Connexion

With Connexion, you write the spec first. Connexion then calls your Python
code, handling the mapping from the specification to the code. This
incentivizes you to write the specification so that all of your
developers can understand what your API does, even before you write a
single line of code.

If multiple teams depend on your APIs, you can use Connexion to easily
send them the documentation of your API. This guarantees that your API will
follow the specification that you wrote. This is a different process from
the one offered by most frameworks, which generate a specification
after you've written the code.
Some disadvantages of generating specifications based on code is that
they often end up lacking details or mix your documentation with the implementation
logic of your application.

(back to top)

โš’๏ธ How to Use

Installation

You can install connexion using pip:

shell
$ pip install connexion

Connexion provides 'extras' with optional dependencies to unlock additional features:

  • swagger-ui: Enables a Swagger UI console for your application.
  • uvicorn: Enables to run the your application using app.run() for
    development instead of using an external ASGI server.
  • flask: Enables the FlaskApp to build applications compatible with the Flask
    ecosystem.

You can install them as follows:

shell
$ pip install connexion[swagger-ui]
    $ pip install connexion[swagger-ui,uvicorn]

(back to top)

Creating your application

Connexion can be used either as a standalone application or as a middleware wrapping an existing
ASGI (or WSGI) application written using a different framework. The standalone application can be
built using either the AsyncApp or FlaskApp.

  • The AsyncApp is a lightweight application with native asynchronous support. Use it if you
    are starting a new project and have no specific reason to use one of the other options.

    python
    from connexion import AsyncApp
    
        app = AsyncApp(__name__)
  • The FlaskApp leverages the Flask framework, which is useful if you're migrating from
    connexion 2.X or you want to leverage the Flask ecosystem.

    python
    from connexion import FlaskApp
    
        app = FlaskApp(__name__)
  • The ConnexionMiddleware can be wrapped around any existing ASGI or WSGI application.
    Use it if you already have an application written in a different framework and want to add
    functionality provided by connexion

    python
    from asgi_framework import App
        from connexion import ConnexionMiddleware
    
        app = App(__name__)
        app = ConnexionMiddleware(app)

(back to top)

Registering an API

While you can register individual routes on your application, Connexion really shines when you
register an API defined by an OpenAPI (or Swagger) specification.
The operation described in your specification is automatically linked to your Python view function via the operationId

run.py

python
def post_greeting(name: str, greeting: str):  # Paramaeters are automatically unpacked
       return f"{greeting} {name}", 200          # Responses are automatically serialized

   app.add_api("openapi.yaml")

openapi.yaml

yaml
...
   paths:
     /greeting/{name}:
       post:
         operationId: run.post_greeting
         responses:
           '200':
             content:
               text/plain:
                 schema:
                   type: string
         parameters:
           - name: name
             in: path
             required: true
             schema:
               type: string
           - name: greeting
             in: query
             required: true
             schema:
               type: string

(back to top)

Running your application

If you installed connexion using connexion[uvicorn], you can run it using the
run method. This is only recommended for development:

python
app.run()

In production, run your application using an ASGI server such as uvicorn. If you defined your
app in a python module called run.py, you can run it as follows:

shell
$ uvicorn run:app

Or with gunicorn:

shell
$ gunicorn -k uvicorn.workers.UvicornWorker run:app

Now you're able to run and use Connexion!

See the examples folder for more examples.

(back to top)

๐Ÿ“œ Changes

A full changelog is maintained on the GitHub releases page.

(back to top)

๐Ÿคฒ Contributing

We welcome your ideas, issues, and pull requests. Just follow the
usual/standard GitHub practices.

For easy development, install connexion using poetry with all extras, and
install the pre-commit hooks to automatically run black formatting and static analysis checks.

shell
pip install poetry
    poetry install --all-extras
    pre-commit install

You can find out more about how Connexion works and where to apply your changes by having a look
at our architecture.

Unless you explicitly state otherwise in advance, any non trivial
contribution intentionally submitted for inclusion in this project by you
to the steward of this repository shall be under the
terms and conditions of Apache License 2.0 written below, without any
additional copyright information, terms or conditions.

(back to top)

๐Ÿ™ Thanks

We'd like to thank all of Connexion's contributors for working on this
project, Swagger/OpenAPI for their support, and Zalando for originally developing and releasing Connexion.

About the advantages of working spec-first:

Tools to help you work spec-first:


File: docs/cli.rst

Command-Line Interface

For convenience Connexion provides a command-line interface
(CLI). This interface aims to be a starting point in developing or
testing OpenAPI specifications with Connexion.

Running an OpenAPI specification

The subcommand run of Connexion's CLI makes it easy to run OpenAPI
specifications directly even before any operation handler function gets
implemented. This allows you to verify and inspect how your API will
work with Connexion.

To run your specification, execute in your shell:

.. code-block:: bash

text
$ connexion run your_api.yaml --stub

This command will tell Connexion to run the your_api.yaml
specification file attaching a stub operation (--stub) to the
unavailable operations/functions of your API, which will return a 501 Not Implemented response.

The basic usage of this command is:

.. code-block:: bash

text
$ connexion run [OPTIONS] SPEC_FILE [BASE_MODULE_PATH]

Where:

  • SPEC_FILE: Your OpenAPI specification file in YAML format. Can also be given
    as a URL, which will be automatically downloaded.
  • BASE_MODULE_PATH (optional): filesystem path where the API endpoints
    handlers are going to be imported from. In short, where your Python
    code is saved.

There are more options available for the run command, for a full
list run:

.. code-block:: bash

text
$ connexion run --help

Running a mock server

You can run a simple server which returns example responses on every request.

The example responses can be defined in the examples response property of
the OpenAPI specification. If no examples are specified, and you have installed connexion with the mock extra (pip install connexion[mock]), an example is generated based on the provided schema.

Your API specification file is not required to have any operationId.

.. code-block:: bash

text
$ connexion run your_api.yaml --mock=all

$ connexion run https://raw.githubusercontent.com/spec-first/connexion/main/examples/helloworld_async/spec/openapi.yaml --mock=all

File: docs/context.rst

Context

The ContextMiddleware included in Connexion provides some information about the current request
context as thread-safe request-level global variables.

You can access them by importing them from connexion.context:

.. code-block:: python

text
from connexion.context import context, operation, receive, request, scope
from connexion import request  # alias for connexion.context.request

Note that when trying to access these context variables outside of the request handling flow, or
without running the ContextMiddleware, the following RuntimeError will be thrown:

.. code-block:: text

text
RuntimeError: Working outside of operation context. Make sure your app is wrapped in a
ContextMiddleware and you're processing a request while accessing the context.

See below for an explanation of the different variables.

request

A Request object representing the incoming request. This is an instance of the
ConnexionRequest.

.. dropdown:: View a detailed reference of the ConnexionRequest class
:icon: eye

text
.. autoclass:: connexion.lifecycle.ConnexionRequest
    :noindex:
    :members:
    :undoc-members:
    :inherited-members:

Some of the methods and attributes are coroutines that can only be accessed from an async
context. When using the FlaskApp, you might want to import the Flask request instead:

.. code-block:: python

text
from flask import request

operation

An Operation object representing the matched operation from your OpenAPI specification.

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    When using OpenAPI 3, this is an instance of the ``OpenAPIOperation`` class.

    .. dropdown:: View a detailed reference of the ``OpenAPIOperation`` class
        :icon: eye

        .. autoclass:: connexion.operations.OpenAPIOperation
            :members:
            :undoc-members:
            :inherited-members:

.. tab-item:: Swagger 2
    :sync: Swagger 2

    When using Swagger 2, this is an instance of the ``Swagger2Operation`` class.

    .. dropdown:: View a detailed reference of the ``Swagger2Operation`` class
        :icon: eye

        .. autoclass:: connexion.operations.Swagger2Operation
            :members:
            :undoc-members:
            :inherited-members:

scope

The ASGI scope as received by the ContextMiddleware, thus containing any changes propagated by
upstream middleware. The ASGI scope is presented as a dict. Please refer to the ASGI spec_
for more information on its contents.

context.context

A dict containing the information from the security middleware:

.. code-block:: python

text
{
    "user": ...  # User information from authentication
    "token_info": ...  # Token information from authentication
}

Third party or custom middleware might add additional fields to this.

receive

.. warning:: Advanced usage

The receive channel as received by the ContextMiddleware. Note that the receive channel might
already be read by other parts of Connexion (eg. when accessing the body via the Request, or
when it is injected into your Python function), and that reading it yourself might make it
unavailable for those parts of the application.

The receive channel can only be accessed from an async context and is therefore not relevant
when using the FlaskApp.

.. _ASGI spec: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope


File: docs/cookbook.rst

Connexion Cookbook

This page provides recipes with Connexion as an ingredient.

CORS

You can enable CORS (Cross-origin resource sharing) by leveraging the CORSMiddleware_ offered by
Starlette. You can add it to your application, ideally in front of the RoutingMiddleware.

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python
    
        from pathlib import Path

        from connexion import AsyncApp
        from connexion.middleware import MiddlewarePosition
        from starlette.middleware.cors import CORSMiddleware


        app = AsyncApp(__name__)

        app.add_middleware(
            CORSMiddleware,
            position=MiddlewarePosition.BEFORE_EXCEPTION,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

        app.add_api("openapi.yaml")

        if __name__ == "__main__":
            app.run(f"{Path(__file__).stem}:app", port=8080)

    .. dropdown:: View a detailed reference of the ``add_middleware`` method
        :icon: eye

        .. automethod:: connexion.AsyncApp.add_middleware
            :noindex:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python
    
        from pathlib import Path

        from connexion import FlaskApp
        from connexion.middleware import MiddlewarePosition
        from starlette.middleware.cors import CORSMiddleware


        app = FlaskApp(__name__)

        app.add_middleware(
            CORSMiddleware,
            position=MiddlewarePosition.BEFORE_EXCEPTION,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

        app.add_api("openapi.yaml")

        if __name__ == "__main__":
            app.run(f"{Path(__file__).stem}:app", port=8080)

    .. dropdown:: View a detailed reference of the ``add_middleware`` method
        :icon: eye

        .. automethod:: connexion.FlaskApp.add_middleware
            :noindex:

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python
    
        from pathlib import Path

        from asgi_framework import App
        from connexion import ConnexionMiddleware
        from starlette.middleware.cors import CORSMiddleware

        app = App(__name__)
        app = ConnexionMiddleware(app)

        app.add_middleware(
            CORSMiddleware,
            position=MiddlewarePosition.BEFORE_EXCEPTION,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

        app.add_api("openapi.yaml")

        if __name__ == "__main__":
            app.run(f"{Path(__file__).stem}:app", port=8080)


    .. dropdown:: View a detailed reference of the ``add_middleware`` method
        :icon: eye

        .. automethod:: connexion.ConnexionMiddleware.add_middleware
            :noindex:

.. _CORSMiddleware: https://www.starlette.io/middleware/#corsmiddleware

Reverse Proxy

When running behind a reverse proxy with stripped path prefix, you need to configure your
application to properly handle this.

Single known path prefix
''''''''''''''''''''''''

If there is only a single known prefix your application will be running behind, you can simply
pass this path prefix as the root_path to your ASGI server:

.. code-block:: bash

text
$ uvicorn run:app --root-path <root_path>

.. code-block:: bash

text
$ gunicorn -k uvicorn.workers.UvicornWorker run:app --root-path <root_path>

Dynamic path prefix
'''''''''''''''''''

If you are running behind multiple proxies, or the path is not known, you can wrap your
application in a ReverseProxied middleware as shown in this example_.

.. _this example: https://github.com/spec-first/connexion/tree/main/examples/reverseproxy


File: docs/exceptions.rst

Exception Handling

Connexion allows you to register custom error handlers to convert Python Exceptions into HTTP
problem responses.

You can register error handlers on:

  • The exception class to handle
    If this exception class is raised somewhere in your application or the middleware stack,
    it will be passed to your handler.
  • The HTTP status code to handle
    Connexion will raise starlette.HTTPException errors when it encounters any issues
    with a request or response. You can intercept these exceptions with specific status codes
    if you want to return custom responses.

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python

        from connexion import AsyncApp
        from connexion.lifecycle import ConnexionRequest, ConnexionResponse

        def not_found(request: ConnexionRequest, exc: Exception) -> ConnexionResponse:
            return ConnexionResponse(status_code=404, body=json.dumps({"error": "NotFound"}))

        app = AsyncApp(__name__)
        app.add_error_handler(FileNotFoundError, not_found)
        app.add_error_handler(404, not_found)

    .. dropdown:: View a detailed reference of the ``add_error_handler`` method
        :icon: eye

        .. automethod:: connexion.AsyncApp.add_error_handler
            :noindex:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python

        from connexion import FlaskApp
        from connexion.lifecycle import ConnexionRequest, ConnexionResponse

        def not_found(request: ConnexionRequest, exc: Exception) -> ConnexionResponse:
            return ConnexionResponse(status_code=404, body=json.dumps({"error": "NotFound"}))

        app = FlaskApp(__name__)
        app.add_error_handler(FileNotFoundError, not_found)
        app.add_error_handler(404, not_found)

    .. dropdown:: View a detailed reference of the ``add_error_handler`` method
        :icon: eye

        .. automethod:: connexion.FlaskApp.add_error_handler
            :noindex:

    .. note::

        .. warning::

            โš ๏ธ **The following is not recommended as it complicates the exception handling logic,**

        You can also register error handlers on the underlying flask application directly.

        .. code-block:: python

            flask_app = app.app
            flask_app.register_error_handler(FileNotFoundError, not_found)
            flask_app.register_error_handler(404, not_found)

        `Flask documentation`_

        Error handlers registered this way:

        - Will only intercept exceptions thrown in the application, not in the Connexion
          middleware.
        - Can intercept exceptions before they reach the error handlers registered on the
          connexion app.
        - When registered on status code, will intercept only
          ``werkzeug.exceptions.HTTPException`` thrown by werkzeug / Flask not
          ``starlette.exceptions.HTTPException``.

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python

        from asgi_framework import App
        from connexion import ConnexionMiddleware
        from connexion.lifecycle import ConnexionRequest, ConnexionResponse

        def not_found(request: ConnexionRequest, exc: Exception) -> ConnexionResponse:
            return ConnexionResponse(status_code=404, body=json.dumps({"error": "NotFound"}))

        app = App(__name__)
        app = ConnexionMiddleware(app)

        app.add_error_handler(FileNotFoundError, not_found)
        app.add_error_handler(404, not_found)

    .. dropdown:: View a detailed reference of the ``add_error_handler`` method
        :icon: eye

        .. automethod:: connexion.ConnexionMiddleware.add_error_handler
            :noindex:

    .. note::

        This might not catch ``HTTPExceptions`` with the same status code raised by
        your wrapped ASGI/WSGI framework.

.. note::

text
Error handlers can be ``async`` coroutines as well.

.. _Flask documentation: https://flask.palletsprojects.com/en/latest/errorhandling/#error-handlers

Default Exception Handling

By default connexion exceptions are JSON serialized according to
Problem Details for HTTP APIs_

Application can return errors using connexion.problem.problem or raise exceptions that inherit
either from connexion.ProblemException or one of its subclasses to achieve the same behavior.

Using this, we can rewrite the handler above:

.. code-block:: python

text
from connexion.lifecycle import ConnexionRequest, ConnexionResponse
from connexion.problem import problem

def not_found(request: ConnexionRequest, exc: Exception) -> ConnexionResponse:
    return problem(
        title="NotFound",
        detail="The requested resource was not found on the server",
        status=404,
    )

.. dropdown:: View a detailed reference of the :code:problem function
:icon: eye

text
.. autofunction:: connexion.problem.problem

.. _Problem Details for HTTP APIs: https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00

Connexion Exceptions

There are several exception types in connexion that contain extra information to help you render appropriate
messages to your user beyond the default description and status code:

.. automodule:: connexion.exceptions
:members:
:show-inheritance:
:member-order: bysource


File: docs/index.rst

.. Connexion documentation master file, created by
sphinx-quickstart on Wed Jun 17 12:09:55 2015.
You can adapt this file completely to your liking, but it should at least
contain the root toctree directive.

.. important::

text
๐Ÿ“ข **Connexion 3 was recently released!**

If you are upgrading from Connexion 2, refer to the :doc:`v3` section.

Welcome to Connexion's documentation!

Connexion is a modern Python web framework that makes spec-first and api-first development easy. You
describe your API in an OpenAPI_ (or swagger) specification with as much detail as you want and
Connexion will guarantee that it works as you specified.

Connexion Features

Based on your specification, Connexion provides the following functionality:

  • Automatic routing to your Python functions
  • Authentication
  • Request validation
  • Parameter parsing and injection
  • Response serialization
  • Response validation
  • A Swagger UI console with live documentation and 'try it out' feature

Connexion also helps you write your OpenAPI specification and develop against it by providing a
command line interface which lets you test and mock your specification.

.. code-block:: bash

text
connexion run openapi.yaml

Sponsors

.. image:: https://raw.githubusercontent.com/spec-first/connexion/main/docs/images/sponsors/ML6.png
:alt: ML6
:height: 100px
:target: https://www.ml6.eu

.. image:: https://raw.githubusercontent.com/spec-first/connexion/main/docs/images/sponsors/Fern.png
:alt: Fern
:height: 100px
:target: https://www.devmark.ai/fern/?utm_source=connexion&utm_loc=website&utm_type=logo

Sponsors help us dedicate time to maintain Connexion. Want to help?

.. raw:: html

Explore the options ยป

|

Why Connexion?

Being spec-first is what makes Connexion unique in the Python ecosystem. With Connexion, you write
your API specification first, and automatically get a lot of functionality. With all other popular
Python web frameworks, you write your functionality first, and automatically get your specification.

We choose the spec-first approach because it:

  • Stimulates thinking about the design of your API and enables quick feedback loops
  • Creates a common understanding of how the API should work
  • Allows server and client development in parallel
  • Enables contract testing
  • Allows for orchestrating multiple layers of your API stack from one contract (eg. API Gateway)

For a more detailed explanation about the benefits of working spec-first, or an overview of helpful
tooling, have a look at our recommended resources_.

Documentation

.. toctree::
:maxdepth: 2

quickstart
routing
swagger_ui
request
response
validation
security
context
lifespan
exceptions
middleware
testing
cli
cookbook
v3

About the advantages of working spec-first:

  • Blog Atlassian_
  • API guidelines Zalando_
  • Blog ML6_
  • Blog Zalando_

Tools to help you work spec-first:

  • Online swagger editor_
  • VS Code plugin_
  • Pycharm plugin_

.. _stable version: https://connexion.readthedocs.io/en/stable/
.. _v3: https://connexion.readthedocs.io/en/latest/v3.html
.. _OpenAPI: https://openapis.org/
.. _Blog atlassian: https://www.atlassian.com/blog/technology/spec-first-api-development
.. _Blog ML6: https://blog.ml6.eu/why-we-decided-to-help-maintain-connexion-c9f449877083
.. _Blog Zalando: https://engineering.zalando.com/posts/2016/12/crafting-effective-microservices-in-python.html
.. _API guidelines Zalando: https://opensource.zalando.com/restful-api-guidelines/#api-first
.. _Online swagger editor: https://editor.swagger.io/
.. _VS Code plugin: https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi
.. _Pycharm plugin: https://plugins.jetbrains.com/plugin/14837-openapi-swagger-editor


File: docs/lifespan.rst

Lifespan

You can register lifespan handlers to run code before the app starts, or after it shuts down.
This ideal for setting up and tearing down database connections or machine learning models for
instance.

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python

        import contextlib
        import typing

        from connexion import AsyncApp, ConnexionMiddleware, request

        @contextlib.asynccontextmanager
        async def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
            """Called at startup and shutdown, can yield state which will be available on the
             request."""
            client = Client()
            yield {"client": client}
            client.close()

        def route():
            """Endpoint function called when receiving a request, you can access the state
            on the request here."""
            client = request.state.client
            client.call()

        app = AsyncApp(__name__, lifespan=lifespan_handler)

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python

        import contextlib
        import typing

        from connexion import FlaskApp, ConnexionMiddleware, request

        @contextlib.asynccontextmanager
        async def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
            """Called at startup and shutdown, can yield state which will be available on the
             request."""
            client = Client()
            yield {"client": client}
            client.close()

        def route():
            """Endpoint function called when receiving a request, you can access the state
            on the request here."""
            client = request.state.client
            client.call()

        app = FlaskApp(__name__, lifespan=lifespan_handler)

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python

        import contextlib
        import typing

        from asgi_framework import App
        from connexion import ConnexionMiddleware, request

        @contextlib.asynccontextmanager
        async def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
            """Called at startup and shutdown, can yield state which will be available on the
             request."""
            client = Client()
            yield {"client": client}
            client.close()

        def endpoint():
            """Endpoint function called when receiving a request, you can access the state
            on the request here."""
            client = request.state.client
            client.call()

        app = App(__name__)
        app = ConnexionMiddleware(app, lifespan=lifespan_handler)

Running lifespan in tests

If you want lifespan handlers to be called during tests, you can use the test_client as a
context manager.

.. code-block:: python

text
def test_homepage():
    app = ...  # Set up app
    with app.test_client() as client:
        # Application's lifespan is called on entering the block.
        response = client.get("/")
        assert response.status_code == 200

    # And the lifespan's teardown is run when exiting the block.

For more information, please refer to the Starlette documentation_.

.. _Starlette documentation: https://www.starlette.io/lifespan/


File: docs/middleware.rst

Middleware

Connexion is built as an ASGI middleware stack wrapping an application. It includes several
middlewares by default that add functionality based on the OpenAPI specification, in the
following order:

.. csv-table::
:widths: 30, 70

text
**ServerErrorMiddleware**, "Returns server errors for any exceptions not caught by the
ExceptionMiddleware"
**ExceptionMiddleware**, Handles exceptions raised by the middleware stack or application
**SwaggerUIMiddleware**, Adds a Swagger UI to your application
**RoutingMiddleware**, "Routes incoming requests to the right operation defined in the
specification"
**SecurityMiddleware**, "Checks incoming requests against the security defined in the
specification"
**RequestValidationMiddleware**, Validates the incoming requests against the spec
**ResponseValidationMiddleware**, "Validates the returned responses against the spec, if
activated"
**LifespanMiddleware**, "Allows registration of code to run before application start-up or
after shut-down"
**ContextMiddleware**, "Makes several request scoped context variables available to the
application"

Adding middleware

You can easily add additional ASGI middleware to the middleware stack with the
:code:add_middleware method:

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python

        from connexion import AsyncApp

        app = AsyncApp(__name__)

        app.add_middleware(MiddlewareClass, **options)

    .. dropdown:: View a detailed reference of the :code:`add_middleware` method
        :icon: eye

        .. automethod:: connexion.AsyncApp.add_middleware
            :noindex:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python

        from connexion import FlaskApp

        app = FlaskApp(__name__)

        app.add_middleware(MiddlewareClass, **options)

    .. dropdown:: View a detailed reference of the :code:`add_middleware` method
        :icon: eye

        .. automethod:: connexion.FlaskApp.add_middleware
            :noindex:

    You can also add WSGI middleware to a ``FlaskApp``. Note that it will only be called at the
    end of the middleware stack. If you need your middleware to act sooner, you will have to
    use an ASGI middleware instead.

    .. code-block:: python

        app.add_wsgi_middleware(MiddlewareClass, **options)

    .. dropdown:: View a detailed reference of the :code:`add_middleware` method
        :icon: eye

        .. automethod:: connexion.FlaskApp.add_wsgi_middleware
            :noindex:

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python

        from asgi_framework import App
        from connexion import ConnexionMiddleware

        app = App(__name__)
        app = ConnexionMiddleware(app)

        app.add_middleware(MiddlewareClass, **options)

    .. dropdown:: View a detailed reference of the :code:`add_wsgi_middleware` method
        :icon: eye

        .. automethod:: connexion.ConnexionMiddleware.add_middleware
            :noindex:

Middleware order


The :code:add_middleware method takes a :code:position argument to define where in the
middleware stack it should be inserted, which should be an instance of the
:class:~connexion.middleware.MiddlewarePosition Enum. The positions below are ordered from
outer to inner, in the order they are hit by incoming requests. Note that responses hit the
middlewares in reversed order.

.. autoclass:: connexion.middleware.MiddlewarePosition
:members:
:member-order: bysource

Customizing the middleware stack

If you need more flexibility, or want to modify or delete any of the default middlewares, you can
also pass in a customized middleware stack when instantiating your application.

For example, if you would like to remove the :class:SecurityMiddleware since you are handling
Security through an API Gateway in front of your application, you can do:

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python

        from connexion import AsyncApp, ConnexionMiddleware
        from connexion.middleware.security import SecurityMiddleware

        middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
                       if middleware is not SecurityMiddleware]

        app = AsyncApp(__name__, middlewares=middlewares)

    .. dropdown:: View a detailed reference of the :class:`~connexion.AsyncApp`
        :code:`__init__` method
        :icon: eye

        .. autoclass:: connexion.AsyncApp
            :noindex:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python

        from connexion import FlaskApp, ConnexionMiddleware
        from connexion.middleware.security import SecurityMiddleware

        middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
                       if middleware is not SecurityMiddleware]

        app = FlaskApp(__name__, middlewares=middlewares)

    .. dropdown:: View a detailed reference of the :class:`~connexion.FlaskApp`
        :code:`__init__` method
        :icon: eye

        .. autoclass:: connexion.FlaskApp
            :noindex:


.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python

        from asgi_framework import App
        from connexion import ConnexionMiddleware
        from connexion.middleware.security import SecurityMiddleware

        middlewares = [middleware for middleware in ConnexionMiddleware.default_middlewares
                       if middleware is not SecurityMiddleware]

        app = App(__name__)
        app = ConnexionMiddleware(app, middlewares=middlewares)

    .. dropdown:: View a detailed reference of the :class:`~connexion.ConnexionMiddleware`
        :code:`__init__` method
        :icon: eye

        .. autoclass:: connexion.ConnexionMiddleware
            :noindex:

Writing custom middleware

You can add any custom middleware as long as it implements the ASGI interface. To learn how to
write pure ASGI middleware, please refer to the documentation of starlette_.

List of useful middleware

Starlette provides a bunch of useful middleware such as:

  • CORSMiddleware_
  • SessionMiddleware_
  • HTTPSRedirectMiddleware_
  • TrustedHostMiddleware_
  • GZipMiddleware_

Other useful middleware:

  • ProxyHeadersMiddleware_ by Uvicorn
  • SentryASGIMiddleware_ by Sentry
  • MetricsMiddleware_ by Prometheus

For more, check the asgi-middleware topic_ on github.

.. _documentation of starlette: https://www.starlette.io/middleware/#writing-pure-asgi-middleware
.. _CORSMiddleware: https://www.starlette.io/middleware/#corsmiddleware
.. _SessionMiddleware: https://www.starlette.io/middleware/#sessionmiddleware
.. _HTTPSRedirectMiddleware: https://www.starlette.io/middleware/#httpsredirectmiddleware
.. _TrustedHostMiddleware: https://www.starlette.io/middleware/#trustedhostmiddleware
.. _GZipMiddleware: https://www.starlette.io/middleware/#gzipmiddleware
.. _ProxyHeadersMiddleware: https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py
.. _SentryASGIMiddleware: https://docs.sentry.io/platforms/python/configuration/integrations/asgi/
.. _MetricsMiddleware: https://github.com/claws/aioprometheus/blob/master/src/aioprometheus/asgi/middleware.py
.. _asgi-middleware topic: https://github.com/topics/asgi-middleware


File: docs/quickstart.rst

Quickstart

Installation

You can install connexion using pip:

.. code-block:: bash

text
$ pip install connexion

Connexion provides 'extras' with optional dependencies to unlock additional features:

  • :code:flask: Enables the :code:FlaskApp to build applications compatible with the Flask
    ecosystem.
  • :code:swagger-ui: Enables a Swagger UI console for your application.
  • :code:uvicorn: Enables to run the your application using :code:app.run() for
    development instead of using an external ASGI server.

You can install them as follows:

.. code-block:: bash

text
$ pip install connexion[<extra>]
$ pip install connexion[<extra1>,<extra2>].

Creating your application

Connexion can be used either as a standalone application or as a middleware wrapping an existing
ASGI (or WSGI) application written using a different framework. The standalone application can be
built using either the :code:AsyncApp or :code:FlaskApp.

  • The :code:AsyncApp is a lightweight application with native asynchronous support. Use it if you
    are starting a new project and have no specific reason to use one of the other options.
  • The :code:FlaskApp leverages the Flask framework, which is useful if you're migrating from
    connexion 2.X or you want to leverage the Flask ecosystem.
  • The :code:ConnexionMiddleware can be wrapped around any existing ASGI or WSGI application.
    Use it if you already have an application written in a different framework and want to add
    functionality provided by connexion

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python
        :caption: **app.py**

        from connexion import AsyncApp

        app = AsyncApp(__name__)

    .. dropdown:: View a detailed reference of the options accepted by the :code:`AsyncApp`
        :icon: eye

        .. autoclass:: connexion.AsyncApp
            :noindex:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. note::
        To leverage the :code:`FlaskApp`, make sure you install connexion using the
        :code:`flask` extra.

    .. code-block:: python
        :caption: **app.py**

        from connexion import FlaskApp

        app = FlaskApp(__name__)

    .. dropdown:: View a detailed reference of the options accepted by the :code:`FlaskApp`
        :icon: eye

        .. autoclass:: connexion.FlaskApp
            :noindex:

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python
        :caption: **app.py**

        from asgi_framework import App
        from connexion import ConnexionMiddleware

        app = App(__name__)
        app = ConnexionMiddleware(app)


    You can also wrap a WSGI application leveraging the :code:`a2wsgi.WSGIMiddleware`:

    .. code-block:: python
        :caption: **app.py**

        from wsgi_framework import App
        from connexion import ConnexionMiddleware
        from a2wsgi import WSGIMiddleware

        wsgi_app = App(__name__)
        asgi_app = WSGIMiddleware(wsgi_app)
        app = ConnexionMiddleware(asgi_app)

    .. dropdown:: View a detailed reference of the options accepted by the
        :code:`ConnexionMiddleware`
        :icon: eye

        .. autoclass:: connexion.ConnexionMiddleware
            :noindex:

Registering an API

While you can register individual routes on your application, connexion really shines when you
register an API defined by an OpenAPI (or Swagger) specification.

.. grid::
:padding: 0

text
.. grid-item:: **run.py**

    .. code-block:: python

        def post_greeting(name: str):
            return f"Hello {name}", 200

        app.add_api("openapi.yaml")

.. grid-item:: **openapi.yaml**

    .. code-block:: yaml

        openapi: "3.0.0"
        info:
          title: Greeting application
          version: 0.0.1
        paths:
          /greeting/{name}:
            post:
              operationId: run.post_greeting
              responses:
                '200':
                  content:
                    text/plain:
                      schema:
                        type: string
              parameters:
                - name: name
                  in: path
                  required: true
                  schema:
                    type: string

The operation described in your specification is automatically linked to your Python view function
via the :code:operationId. You can change this behavior using different :code:Resolvers, see
:doc:routing. When the endpoint is called, connexion will take care of routing, security,
request body and parameter parsing, and response serialization. All based on the specification.

You can add as many APIs as you want to a single application. The :code:add_api() method
provides a lot of configuration options. When an option is provided both to the App and the API,
the API value will take precedence.

.. dropdown:: View a detailed reference of the options accepted by the :code:add_api() method
:icon: eye

text
.. tab-set::

    .. tab-item:: AsyncApp
        :sync: AsyncApp

        .. autofunction:: connexion.AsyncApp.add_api
            :noindex:

    .. tab-item:: FlaskApp
        :sync: FlaskApp

        .. autofunction:: connexion.FlaskApp.add_api
            :noindex:

    .. tab-item:: ConnexionMiddleware
        :sync: ConnexionMiddleware

        .. autofunction:: connexion.ConnexionMiddleware.add_api
            :noindex:

Running your application

You can run your application using an ASGI server such as uvicorn. If you defined your
:code:app in a python module called :code:run.py, you can run it as follows:

.. code-block:: bash

text
# assuming your application is defined as ``app`` in ``run.py``
$ uvicorn run:app

Or with gunicorn (which is recommended in production).

.. code-block:: bash

text
# assuming your application is defined as ``app`` in ``run.py``
$ gunicorn -k uvicorn.workers.UvicornWorker run:app

See the uvicorn documentation_ for more details or check this
overview of available ASGI servers_ for other options.

.. _overview of available ASGI servers: https://asgi.readthedocs.io/en/latest/implementations.html#servers

If you installed connexion using :code:connexion[uvicorn], you can run it using the
:code:run method. This is only recommended for development:

.. code-block:: python

text
app.run()

To leverage automatic reloading of your application, you need to provide the application as an
import string. In most cases, this can be achieved as follows:

.. code-block:: python

text
from pathlib import Path

app.run(f"{Path(__file__).stem}:app")

.. dropdown:: View a detailed reference of the options accepted by the :code:run() method
:icon: eye

text
.. tab-set::

    .. tab-item:: AsyncApp
        :sync: AsyncApp

        .. autofunction:: connexion.AsyncApp.run
            :noindex:

    .. tab-item:: FlaskApp
        :sync: FlaskApp

        .. autofunction:: connexion.FlaskApp.run
            :noindex:

    .. tab-item:: ConnexionMiddleware
        :sync: ConnexionMiddleware

        .. autofunction:: connexion.ConnexionMiddleware.run
            :noindex:

.. _uvicorn documentation: https://www.uvicorn.org/deployment/

The Swagger UI

If you installed connexion using the :code:swagger-ui extra, a Swagger UI is available for each
API, providing interactive documentation. By default the UI is hosted at :code:{base_path}/ui/
where :code:`base_path`` is the base path of the API.

https://{host}/{base_path}/ui/

.. image:: images/swagger_ui.png

Check :doc:swagger_ui for information on how to configure the UI.

Full App class reference

For more details on what you can do, view the complete API reference below.

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. dropdown:: View a detailed reference of the :code:`AsyncApp`
        :icon: eye

        .. autoclass:: connexion.AsyncApp
            :members:
            :undoc-members:
            :inherited-members:

.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. dropdown:: View a detailed reference of the :code:`FlaskApp`
        :icon: eye

        .. autoclass:: connexion.FlaskApp
            :members:
            :undoc-members:
            :inherited-members:

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. dropdown:: View a detailed reference of the :code:`ConnexionMiddleware`
        :icon: eye

        .. autoclass:: connexion.ConnexionMiddleware
            :members:
            :undoc-members:

File: docs/request.rst

Request handling

When your application receives a request, Connexion provides a lot of functionality based on your
OpenAPI spec:

  • It checks the security (see :doc:security)
  • It routes the request to the correct endpoint (see :doc:routing)
  • It validates the body and parameters (see :doc:validation)
  • It parses and passes the body and parameters to your python function

On this page, we zoom in on the final part.

Automatic parameter handling

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    Connexion automatically maps the parameters defined in your endpoint specification to the
    arguments defined your associated Python function, parsing and casting values when
    possible. All you need to do, is make sure the arguments of your function match the
    parameters in your specification.

.. tab-item:: FlaskApp
    :sync: FlaskApp

    Connexion automatically maps the parameters defined in your endpoint specification to the
    arguments defined your associated Python function, parsing and casting values when
    possible. All you need to do, is make sure the arguments of your function match the
    parameters in your specification.

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    Connexion can automatically map the parameters defined in your endpoint specification to
    the arguments defined your associated Python function, parsing and casting values when
    possible. All you need to do, is make sure the arguments of your function match the
    parameters in your specification.

    To activate this behavior when using the ``ConnexionMiddleware`` wrapping a third party
    application, you can leverage the following decorators provided by Connexion:

    * ``WSGIDecorator``: provides automatic parameter injection for WSGI applications. Note
      that this decorator injects Werkzeug / Flask datastructures.

    * ``FlaskDecorator``: provides automatic parameter injection and response serialization for
      Flask applications.

    * ``ASGIDecorator``: provides automatic parameter injection for ASGI applications. Note that
      this decorator injects Starlette datastructures (such as UploadFile).

    * ``StarletteDecorator``: provides automatic parameter injection and response serialization
      for Starlette applications.

    .. code-block:: python
        :caption: **app.py**

        from asgi_framework import App
        from connexion import ConnexionMiddleware
        from connexion.decorators import ASGIDecorator

        @app.route("/greeting/<name>", methods=["POST"])
        @ASGIDecorator()
        def post_greeting(name):
            ...

        app = App(__name__)
        app = ConnexionMiddleware(app)
        app.add_api("openapi.yaml")

    For a full example, see our `Frameworks`_ example.

For example, if you have an endpoint specified as:

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    .. code-block:: yaml
        :caption: **openapi.yaml**

        paths:
          /foo:
            get:
              operationId: api.foo_get
              parameters:
                - name: message
                  in: query
                  schema:
                    type: string
                  required: true

.. tab-item:: Swagger 2
    :sync: Swagger 2

    .. code-block:: yaml
        :caption: **swagger.yaml**

        paths:
          /foo:
            get:
              operationId: api.foo_get
              parameters:
                - name: message
                  in: query
                  type: string
                  required: true

And the view function as:

.. code-block:: python
:caption: api.py

text
def foo_get(message):
    ...

Connexion will automatically identify that your view function expects an argument named message
and will pass in the value of the endpoint parameter message.

This works for both path and query parameters.

Body

The body will also be passed to your function.

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    In the OpenAPI 3 spec, the ``requestBody`` does not have a name. By default it will be
    passed into your function as ``body``. You can use ``x-body-name`` in your operation to
    override this name.

    .. code-block:: yaml
        :caption: **openapi.yaml**

        paths:
          /foo:
            post:
              operationId: api.foo_get
              requestBody:
                x-body-name: payload
                content:
                  application/json:
                    schema:
                      ...

    .. code-block:: python
        :caption: **api.py**

        # Default
        def foo_get(body)
            ...

        # Based on x-body-name
        def foo_get(payload)
            ...

.. tab-item:: Swagger 2
    :sync: Swagger 2

    In the Swagger 2 specification, you can define the name of your body. Connexion will pass
    the body to your function using this name.

    .. code-block:: yaml
        :caption: **swagger.yaml**

        paths:
          /foo:
            post:
              consumes:
                - application/json
                parameters:
                  - in: body
                    name: payload
                    schema:
                      ...

    .. code-block:: python
        :caption: **api.py**

        def foo_get(payload)
            ...

    Form data
    `````````

    In Swagger 2, form data is defined as parameters in your specification, and Connexion
    passes these parameters individually:


    .. code-block:: yaml
        :caption: **swagger.yaml**

        paths:
          /foo:
            post:
              operationId: api.foo_get
              consumes:
                - application/json
              parameters:
                - in: formData
                  name: field1
                  type: string
                - in: formData
                  name: field2
                  type: string

    .. code-block:: python
        :caption: **api.py**

        def foo_get(field1, field2)
            ...

Connexion will not automatically pass in the default values defined in your requestBody
definition, but you can activate this by configuring a different
:ref:RequestBodyValidator<validation:Custom validators>.

Files

Connexion extracts the files from the body and passes them into your view function separately:

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    .. code-block:: yaml
        :caption: **openapi.yaml**

        paths:
          /foo:
            post:
              operationId: api.foo_get
              requestBody:
                content:
                  multipart/form-data:
                    schema:
                      type: object
                      properties:
                        file:
                          type: string
                          format: binary

.. tab-item:: Swagger 2
    :sync: Swagger 2

    In the Swagger 2 specification, you can define the name of your body. Connexion will pass
    the body to your function using this name.

    .. code-block:: yaml
        :caption: **swagger.yaml**

        paths:
          /foo:
            post:
              consumes:
                - application/json
              parameters:
                - name: file
                  type: file
                  in: formData

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    If you're using the `AsyncApp`, the files are provided as `Starlette.UploadFile`_ instances.

    .. code-block:: python
        :caption: **api.py**

                def foo_get(file)
                    assert isinstance(file, starlette.UploadFile)
                    ...


.. tab-item:: FlaskApp
    :sync: FlaskApp

    If you're using the `FlaskApp`, the files are provided as `werkzeug.FileStorage`_ instances.

    .. code-block:: python
        :caption: **api.py**

                def foo_get(file)
                    assert isinstance(file, werkzeug.FileStorage)
                    ...

When your specification defines an array of files:

.. code-block:: yaml

text
type: array
items:
    type: string
    format: binary

They will be provided to your view function as a list.

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python
        :caption: **api.py**

                def foo_get(file)
                    assert isinstance(file, list)
                    assert isinstance(file[0], starlette.UploadFile)
                    ...


.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python
        :caption: **api.py**

                def foo_get(file)
                    assert isinstance(file, list)
                    assert isinstance(file[0], werkzeug.FileStorage)
                    ...

.. _Starlette.UploadFile: https://www.starlette.io/requests/#request-files
.. _werkzeug.FileStorage: https://werkzeug.palletsprojects.com/en/3.0.x/datastructures/#werkzeug.datastructures.FileStorage

Optional arguments & Defaults

If a default value is defined for a parameter in the OpenAPI specification, Connexion will
automatically pass it in if no value was included in the request. If a default is defined in the
specification, you should not define a default in your Python function, as it will never be
triggered.

If an endpoint parameter is optional and no default is defined in the specification, you should
make sure the corresponding argument is optional in your Python function as well, by assigning a
default value:

.. code-block:: python
:caption: api.py

text
def foo_get(optional_argument=None)
    ...

Missing arguments and kwargs

Connexion will inspect your function signature and only pass in the arguments that it defines. If
an argument is defined in your specification, but not in your function, Connexion will ignore it.

If you do define a **kwargs argument in your function signature, Connexion will pass in all
arguments, and the ones not explicitly defined in your signature will be collected in the
kwargs argument.

Parameter Name Sanitation

The names of query and form parameters, as well as the name of the body
parameter are sanitized by removing characters that are not allowed in Python
symbols. I.e. all characters that are not letters, digits or the underscore are
removed, and finally characters are removed from the front until a letter or an
underscore is encountered. As an example:

.. code-block:: python

text
>>> re.sub('^[^a-zA-Z_]+', '', re.sub('[^0-9a-zA-Z_]', '', '$top'))
'top'

Pythonic parameters

You can activate Pythonic parameters by setting the pythonic_params option to True on
either the application or the API:

.. tab-set::

text
.. tab-item:: AsyncApp
    :sync: AsyncApp

    .. code-block:: python
        :caption: **app.py**

        from connexion import AsyncApp

        app = AsyncApp(__name__, pythonic_params=True)
        app.add_api("openapi.yaml", pythonic_params=True)


.. tab-item:: FlaskApp
    :sync: FlaskApp

    .. code-block:: python
        :caption: **app.py**

        from connexion import FlaskApp

        app = FlaskApp(__name__, pythonic_params=True)
        app.add_api("openapi.yaml", pythonic_params=True):

.. tab-item:: ConnexionMiddleware
    :sync: ConnexionMiddleware

    .. code-block:: python
        :caption: **app.py**

        from asgi_framework import App
        from connexion import ConnexionMiddleware

        app = App(__name__)
        app = ConnexionMiddleware(app, pythonic_params=True)
        app.add_api("openapi.yaml", pythonic_params=True)

This does two things:

  • CamelCase arguments are converted to snake_case
  • If the argument name matches a Python builtin, an underscore is appended.

When pythonic_params is activated, the following specification:

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    .. code-block:: yaml
        :caption: **openapi.yaml**

        paths:
          /foo:
            get:
              operationId: api.foo_get
              parameters:
                - name: filter
                  in: query
                  schema:
                    type: string
                  required: true
                - name: FilterOption
                  in: query
                  schema:
                    type: string

.. tab-item:: Swagger 2
    :sync: Swagger 2

    .. code-block:: yaml
        :caption: **swagger.yaml**

        paths:
          /foo:
            get:
              operationId: api.foo_get
              parameters:
                - name: filter
                  in: query
                  type: string
                  required: true
                - name: FilterOption
                  in: query
                  type: string

Maps to the following Python function:

.. code-block:: python
:caption: api.py

text
def foo_get(filter_, filter_option=None):
    ...

Type casting

Whenever possible Connexion will try to parse your argument values and cast them to the correct
Python type:

+--------------+-------------+
| OpenAPI Type | Python Type |
| | |
+==============+=============+
| integer | int |
+--------------+-------------+
| string | str |
+--------------+-------------+
| number | float |
+--------------+-------------+
| boolean | bool |
+--------------+-------------+
| array | list |
+--------------+-------------+
| object | dict |
+--------------+-------------+
| null | None |
+--------------+-------------+

Parameter serialization

Array and object parameters need to be serialized into lists and dicts.

.. tab-set::

text
.. tab-item:: OpenAPI 3
    :sync: OpenAPI 3

    The `OpenAPI 3 specification`_ defines the `style` and `explode` keywords which specify how
    these parameters should be serialized.

    To handle these, Connexion provides the ``OpenAPIUriParser`` class, which is enabled by
    default when using an OpenAPI 3 spec.

    Not all combinations of `style` and `explode` are supported yet. Please open an `issue`_ if
    you run into any problems.

.. tab-item:: Swagger 2
    :sync: Swagger 2

    The `Swagger 2 specification`_ defines the `collectionFormat` keyword to specify how
    these parameters should be serialized.

    To handle this for you, Connexion provides the ``Swagger2URIParser`` class, which is
    enabled by default when using a Swagger 2 spec. It currently supports the `pipes`, `csv`,
    and `multi` collection formats.

    This parser adheres to the Swagger 2.0 spec, and will only join together multiple instance
    of the same query parameter if the collectionFormat is set to `multi`. Query parameters
    are parsed from left to right, so if a query parameter is defined twice, then the
    right-most definition wins. For example, if you provided a URI with the query string
    ``?letters=a,b,c&letters=d,e,f`` and ``collectionFormat: csv``, then connexion will set
    ``letters = ['d', 'e', 'f']``.

    Connexion also provides two alternative parsers:

    * The ``FirstValueURIParser``, which behaves like the ``Swagger2URIParser``, except that it
      prefers the first defined value.
    * The ``AlwaysMultiURIParser``, which behaves like the ``Swagger2URIParser``, except that
      it always joins together multiple instances of the same query parameter.

Context

Connexion can pass in some additional context. By default, this contains the following information:

.. code-block:: python

text
{
    "api_base_path": ...  # The base path of the matched API
    "operation_id": ...  # The operation id of matched operation
    "user": ...  # User information from authentication
    "token_info": ...  # Token information from authentication
}

Third party or custom middleware might add additional fields to this.

To receive this in your function, you can either:

  • Specify the context_ argument in your function signature, and the context dict will be
    passed in as a whole:

    .. code-block:: python
    :caption: api.py

    def foo_get(context_):
    ...

  • Specify the keys individually in your function signature:

    .. code-block:: python
    :caption: api.py

    def foo_get(user, token_info):
    ...

Request object

Connexion also exposes a Request class which holds all the information about the incoming
request.

.. code-block:: python

text
from connexion import request

.. dropdown:: View a detailed reference of the connexion.request class
:icon: eye

text
.. warning::

    The asynchronous body arguments (body, form, files) might already be consumed by connexion.
    We recommend to let Connexion inject them into your view function as mentioned above.

.. autoclass:: connexion.lifecycle.ConnexionRequest
   :members:
   :undoc-members:
   :inherited-members:

.. _Frameworks: https://github.com/spec-first/connexion/tree/main/examples/frameworks
.. _OpenAPI 3 specification: https://swagger.io/docs/specification/serialization
.. _Swagger 2 specification: https://swagger.io/docs/specification/2-0/describing-parameters/#array
.. _issue: https://github.com/spec-first/connexion/issues