Routing

Connexion leverages your OpenAPI contract to route requests to your python functions. This can be done in two ways:

Explicit routing

Connexion uses the operation_id to link each operation in your API contract to the python function that should handle it.

openapi.yaml
paths:
  /hello_world:
    post:
      operationId: myapp.api.hello_world

Based on the operationId above, any POST request to http://{HOST}/hello_world, will be handled by the hello_world function in the myapp.api module.

Optionally, you can include x-openapi-router-controller or x-swagger-router-controller in your operationId to make your operationId relative:

openapi.yaml
paths:
  /hello_world:
    post:
      x-openapi-router-controller: myapp.api
      operationId: hello_world

If all your operations are relative, you can use the RelativeResolver class when registering your API instead of repeating the same x-openapi-router-controller in every operation:

app.py
import connexion
from connexion.resolver import RelativeResolver

app = connexion.AsyncApp(__name__)
app.add_api('openapi.yaml', resolver=RelativeResolver('myapp.api'))
View a detailed reference of the RelativeResolver class
class connexion.resolver.RelativeResolver(root_path, function_resolver=<function get_function_from_name>)

Resolves endpoint functions relative to a given root path or module.

Parameters:
  • root_path (Union[str, types.ModuleType]) – The root path relative to which an operationId is resolved. Can also be a module. Has the same effect as setting x-swagger-router-controller or x-openapi-router-controller equal to root_path for every operation individually.

  • function_resolver (types.FunctionType) – Function that resolves functions using an operationId

Note that HEAD requests will be handled by the operationId specified under the GET operation in the specification. Connexion.request.method can be used to determine which request was made. See ConnexionRequest.

Automatic routing

Connexion can also automate the routing for you. You can choose from different Resolvers implementing different resolution strategies.

RestyResolver

The RestyResolver will infer an operationId based on the path and HTTP method of each operation in your specification:

app.py
import connexion
from connexion.resolver import RestyResolver

app = connexion.FlaskApp(__name__)
app.add_api('openapi.yaml', resolver=RestyResolver('api'))
openapi.yaml
paths:
  /:
    get:
       # Implied operationId: api.get
  /foo:
    get:
       # Implied operationId: api.foo.search
    post:
       # Implied operationId: api.foo.post
  /foo/{id}:
    get:
       # Implied operationId: api.foo.get
    put:
       # Implied operationId: api.foo.put
    copy:
       # Implied operationId: api.foo.copy
    delete:
       # Implied operationId: api.foo.delete
  /foo/{id}/bar:
    get:
       # Implied operationId: api.foo.bar.search
  /foo/{id}/bar/{name}:
    get:
       # Implied operationId: api.foo.bar.get

RestyResolver will give precedence to any operationId encountered in the specification and respects x-openapi-router-controller and x-swagger-router-controller.

View a detailed reference of the RestyResolver class
class connexion.resolver.RestyResolver(default_module_name: str, *, collection_endpoint_name: str = 'search')

Resolves endpoint functions using REST semantics (unless overridden by specifying operationId)

Parameters:
  • default_module_name – Default module name for operations

  • collection_endpoint_name – Name of function to resolve collection endpoints to

MethodResolver

The MethodResolver works like a RestyResolver, but routes to class methods instead of functions.

app.py
import connexion
from connexion.resolver import MethodResolver

app = connexion.FlaskApp(__name__)
app.add_api('openapi.yaml', resolver=MethodResolver('api'))
openapi.yaml
paths:
  /foo:
  get:
    # Implied operationId: api.FooView.search
  post:
    # Implied operationId: api.FooView.post
  '/foo/{id}':
  get:
    # Implied operationId: api.FooView.get
  put:
    # Implied operationId: api.FooView.put
  copy:
    # Implied operationId: api.FooView.copy
  delete:
    # Implied operationId: api.FooView.delete

The structure expects a Class to exists inside the api module with the name <<CapitalisedPath>>View.

api.py
class PetsView:

  def post(self, body: dict):
    ...

  def put(self, petId, body: dict):
    ...

  def delete(self, petId):
    ...

  def get(self, petId=None):
    ...

  def search(limit=100):
    ...

It is possible to use decorators for the Method view by listing them in the decorator attribute of the class:

api.py
def example_decorator(f):

    def decorator(*args, **kwargs):
        return f(*args, **kwargs)

    return decorator

class PetsView:
  """Create Pets service"""

  decorators = [example_decorator]

  ...

Additionally, you may inject dependencies into the class by declaring parameters for this class in the __init__ method and providing the arguments in the MethodViewResolver() call. The arguments are passed down to the class when as_view is called.

A class might look like this:

api.py
class PetsView:
    def __init__(self, pets):
        self.pets = pets

And the arguments are provided like this:

app.py
MethodViewResolver("api", class_arguments={"PetsView": {"kwargs": {"pets": zoo}}})

MethodResolver will give precedence to any operationId encountered in the specification and respects x-openapi-router-controller and x-swagger-router-controller.

View a detailed reference of the MethodResolver class
class connexion.resolver.MethodResolver(*args, class_arguments: Optional[Dict[str, Dict[str, Union[Iterable, Dict[str, Any]]]]] = None, **kwargs)

A generic method resolver that instantiates a class and extracts the method from it, based on the operation id.

Parameters:
  • args – Arguments passed to RestyResolver

  • class_arguments – Arguments to instantiate the View Class in the format below

  • kwargs – Keywords arguments passed to RestyResolver

{
  "ViewName": {
    "args": (positional arguments,)
    "kwargs": {
      "keyword": "argument"
    }
  }
}

MethodViewResolver

The MethodResolver works like a MethodViewResolver, but routes to class methods of a Flask MethodView subclass.

Note

If you migrate from connexion v2 you may want to use the MethodResolver in order to maintain the old behavior. The behavior described here is the new behavior, introduced in connexion v3. Previously, in v2, the MethodViewResolver worked like the MethodResolver in v3.

Another difference is that the MethodResolver will look for search and get methods for collection and single item operations respectively, while MethodViewResolver handles both collection and single item operations via the same get method.

app.py
import connexion
from connexion.resolver import MethodResolver

app = connexion.FlaskApp(__name__)
app.add_api('openapi.yaml', resolver=MethodViewResolver('api'))
openapi.yaml
paths:
  /foo:
  get:
    # Implied operationId: api.FooView.get
  post:
    # Implied operationId: api.FooView.post
  '/foo/{id}':
  get:
    # Implied operationId: api.FooView.get
  put:
    # Implied operationId: api.FooView.put
  copy:
    # Implied operationId: api.FooView.copy
  delete:
    # Implied operationId: api.FooView.delete

The structure expects a Class to exists inside the api module with the name <<CapitalisedPath>>View.

api.py
from flask.views import MethodView


class PetsView(MethodView):

  def post(self, body: dict):
    ...

  def put(self, petId, body: dict):
    ...

  def delete(self, petId):
    ...

  def get(self, petId=None, limit=100):
    ...
View a detailed reference of the MethodViewResolver class
class connexion.resolver.MethodViewResolver(*args, **kwargs)

A specialized method resolver that works with flask’s method views. It resolves the method by calling as_view on the class.

Parameters:
  • args – Arguments passed to RestyResolver

  • class_arguments – Arguments to instantiate the View Class in the format below

  • kwargs – Keywords arguments passed to RestyResolver

{
  "ViewName": {
    "args": (positional arguments,)
    "kwargs": {
      "keyword": "argument"
    }
  }
}

Custom resolver

You can import and extend connexion.resolver.Resolver to implement your own operationId and function resolution algorithm.

View a detailed reference of the RestyResolver class
class connexion.resolver.Resolver(function_resolver: ~typing.Callable = <function get_function_from_name>)

Standard resolver

Parameters:

function_resolver – Function that resolves functions using an operationId

resolve(operation)

Default operation resolver

resolve_function_from_operation_id(operation_id)

Invokes the function_resolver

resolve_operation_id(operation)

Default operationId resolver

Note

If you implement a custom Resolver, and think it would be valuable for other users, we would appreciate it as a contribution.

Resolver error

By default, Connexion strictly enforces the presence of a handler function for any path defined in your specification. Because of this, adding new paths without implementing a corresponding handler function will produce runtime errors and your application will not start. To allow new paths to be added to your specification, e.g. in an API design first workflow, set the resolver_error to configure Connexion to provide an error response for paths that are not yet implemented:

app.py
app = connexion.FlaskApp(__name__)
app.add_api('openapi.yaml', resolver_error=501)

Path parameters

Path parameters are variable parts of a URL path denoted with curly braces { } in the specification.

openapi.yaml
paths:
  /users/{id}:
    parameters:
      - in: path
        name: id   # Note the name is the same as in the path
        required: true
        schema:
          type: integer
        description: The user ID
swagger.yaml
paths:
  /users/{id}:
    parameters:
      - in: path
        name: id   # Note the name is the same as in the path
        required: true
        type: integer
        description: The user ID.

By default this will capture characters up to the end of the path or the next /.

You can use convertors to modify what is captured. The available convertors are:

  • str returns a string, and is the default.

  • int returns a Python integer.

  • float returns a Python float.

  • path returns the rest of the path, including any additional / characters.

Convertors are used by defining them as the format in the parameter specification

Specify a route parameter’s type as integer or number or its type as string and its format as path to use these converters.

Path parameters are passed as arguments to your python function.

Individual paths

You can also add individual paths to your application which are not described in your API contract. This can be useful for eg. /healthz or similar endpoints.

api.py
@app.route("/healthz")
def healthz():
    return 200

# Or as alternative to the decorator
app.add_url_rule("/healthz", "healthz", healthz)
View a detailed reference of the route and add_url_rule methods
AsyncApp.route(rule: str, **options)

A decorator that is used to register a view function for a given URL rule. This does the same thing as add_url_rule but is intended for decorator usage:

@app.route('/')
def index():
    return 'Hello World'
Parameters:
  • rule – the URL rule as string

  • options – the options to be forwarded to the underlying werkzeug.routing.Rule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GET, POST etc.). By default a rule just listens for GET (and implicitly HEAD).

AsyncApp.add_url_rule(rule, endpoint: Optional[str] = None, view_func: Optional[Callable] = None, **options)

Connects a URL rule. Works exactly like the route decorator.

Basically this example:

@app.route('/')
def index():
    pass

Is equivalent to the following:

def index():
    pass
app.add_url_rule('/', 'index', index)

Internally`route` invokes add_url_rule so if you want to customize the behavior via subclassing you only need to change this method.

Parameters:
  • rule – the URL rule as string.

  • endpoint – the name of the endpoint for the registered URL rule, which is used for reverse lookup. Flask defaults to the name of the view function.

  • view_func – the function to call when serving a request to the provided endpoint.

  • options – the options to be forwarded to the underlying werkzeug.routing.Rule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GET, POST etc.). By default a rule just listens for GET (and implicitly HEAD).

View a detailed reference of the route and add_url_rule methods
FlaskApp.route(rule: str, **options)

A decorator that is used to register a view function for a given URL rule. This does the same thing as add_url_rule but is intended for decorator usage:

@app.route('/')
def index():
    return 'Hello World'
Parameters:
  • rule – the URL rule as string

  • options – the options to be forwarded to the underlying werkzeug.routing.Rule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GET, POST etc.). By default a rule just listens for GET (and implicitly HEAD).

FlaskApp.add_url_rule(rule, endpoint: Optional[str] = None, view_func: Optional[Callable] = None, **options)

Connects a URL rule. Works exactly like the route decorator.

Basically this example:

@app.route('/')
def index():
    pass

Is equivalent to the following:

def index():
    pass
app.add_url_rule('/', 'index', index)

Internally`route` invokes add_url_rule so if you want to customize the behavior via subclassing you only need to change this method.

Parameters:
  • rule – the URL rule as string.

  • endpoint – the name of the endpoint for the registered URL rule, which is used for reverse lookup. Flask defaults to the name of the view function.

  • view_func – the function to call when serving a request to the provided endpoint.

  • options – the options to be forwarded to the underlying werkzeug.routing.Rule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GET, POST etc.). By default a rule just listens for GET (and implicitly HEAD).

When using the ConnexionMiddleware around an ASGI or WSGI application, you can register individual routes on the wrapped application.

API Versioning and basePath

Setting a base path is useful for versioned APIs. An example of a base path would be the 1.0 in http://{HOST}/1.0/hello_world.

If you are using OpenAPI 3, you set your base URL path in the servers block of the specification. You can either specify a full URL, or just a relative path.

openapi.yaml
servers:
  - url: https://{{HOST}}/1.0
    description: full url example
  - url: /1.0
    description: relative path example

paths:
  ...

If you are using Swagger 2.0, you can define a basePath on the top level of your Swagger 2.0 specification.

swagger.yaml
basePath: /1.0

paths:
  ...

If you don’t want to include the base path in your specification, you can provide it when adding the API to your application:

app.py
app.add_api('openapi.yaml', base_path='/1.0')