## File: README.md

<a id="top"></a>
 
 

---

Connexion is a modern Python web framework that makes spec-first and api-first development easy.
You describe your API in an [OpenAPI][OpenAPI] (or [Swagger][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](#top))


## 🫶 Sponsors

[](https://www.ml6.eu)

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

[**Explore the options »**](https://github.com/sponsors/spec-first)

([back to top](#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](#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](#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](#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](#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][examples] folder for more examples.

([back to top](#top))

## 📜 Changes

A full changelog is maintained on the [GitHub releases page][Releases].

([back to top](#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][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](#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.

## 📚 Recommended Resources

About the advantages of working spec-first:

* [Blog Atlassian][Blog Atlassian]
* [API guidelines Zalando][API guidelines Zalando]
* [Blog ML6][Blog ML6]
* [Blog Zalando][Blog Zalando]

Tools to help you work spec-first:

* [Online swagger editor][Online swagger editor]
* [VS Code plugin][VS Code plugin]
* [Pycharm plugin][Pycharm plugin]

[OpenAPI]: https://openapis.org/
[Swagger]: http://swagger.io/open-source-integrations/
[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
[examples]: https://github.com/spec-first/connexion/blob/main/examples
[Releases]: https://github.com/spec-first/connexion/releases
[Architecture]: https://github.com/spec-first/connexion/blob/main/docs/images/architecture.png

---

## 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

     $ 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

    $ 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

     $ 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

    $ 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

    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

    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

    .. 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

    from flask import request

operation
---------

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

.. tab-set::

    .. 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

    {
        "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::

    .. 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

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

.. code-block:: bash

    $ 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::

    .. 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::

    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

    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

    .. 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::

    📢 **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

    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 »**](https://github.com/sponsors/spec-first)

|

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

Recommended resources
---------------------

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::

    .. 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

    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

    **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::

    .. 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::

    .. 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

    $ 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

    $ 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::

    .. 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

    .. 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

    .. 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

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

Or with gunicorn (which is recommended in production).

.. code-block:: bash

    # 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

    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

    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

    .. 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::

    .. 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::

    .. 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::

    .. 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**

    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::

    .. 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::

    .. 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::

    .. 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

    type: array
    items:
        type: string
        format: binary

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

.. tab-set::

    .. 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**

    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

    >>> 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::

    .. 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::

    .. 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**

    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::

    .. 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

    {
        "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

    from connexion import request

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

    .. 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