Connexion is a framework on top of Flask that automagically handles HTTP requests based on OpenAPI 2.0 Specification (formerly known as Swagger Spec) of your API described in YAML format. Connexion allows you to write a Swagger specification, then maps the endpoints to your Python functions; this makes it unique, as many tools generate the specification based on your Python code. You can describe your REST API in as much detail as you want; then Connexion guarantees that it will work as you specified.
We built Connexion this way in order to:
- simplify the development process
- confirm expectations about what your API will look like
- Validates requests and endpoint parameters automatically, based on your specification
- Provides a Web Swagger Console UI so that the users of your API can have live documentation and even call your API's endpoints through it
- Handles OAuth 2 token-based authentication
- Supports API versioning
- Supports automatic serialization of payloads. If your specification defines that an endpoint returns JSON, Connexion will automatically serialize the return value for you and set the right content type in the HTTP header.
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 that offered by frameworks such as Hug, which generates 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 code logic of your application.
- Zalando Tech blog post API First
- Connexion listed on Swagger's website
- Blog post: Crafting effective Microservices in Python
Python 2.7 or Python 3.4+
In your command line, type:
$ pip install connexion
Place your API YAML inside a folder in the root
path of your application (e.g swagger/
). Then run:
import connexion
app = connexion.App(__name__, specification_dir='swagger/')
app.add_api('my_api.yaml')
app.run(port=8080)
See the Connexion Pet Store Example Application for a sample specification.
Now you're able to run and use Connexion!
Connexion supports one of the three OAuth 2 handling methods. (See
"TODO" below.) With Connexion, the API security definition must
include a 'x-tokenInfoUrl' or 'x-tokenInfoFunc (or set TOKENINFO_URL
or TOKENINFO_FUNC
env var respectively). 'x-tokenInfoUrl' must contain an
URL to validate and get the token information and 'x-tokenInfoFunc must
contain a reference to a function used to obtain the token info. When both 'x-tokenInfoUrl'
and 'x-tokenInfoFunc' are used, Connexion will prioritize the function method. Connexion expects to
receive the OAuth token in the Authorization
header field in the
format described in RFC 6750 section 2.1. This aspect
represents a significant difference from the usual OAuth flow.
Connexion uses Jinja2 to allow specification parameterization through the arguments
parameter. You can define specification arguments for the application either globally (via the connexion.App
constructor) or for each specific API (via the connexion.App#add_api
method):
app = connexion.App(__name__, specification_dir='swagger/',
arguments={'global': 'global_value'})
app.add_api('my_api.yaml', arguments={'api_local': 'local_value'})
app.run(port=8080)
When a value is provided both globally and on the API, the API value will take precedence.
Connexion uses the operationId
from each Operation Object to
identify which Python function should handle each URL.
Explicit Routing:
paths:
/hello_world:
post:
operationId: myapp.api.hello_world
If you provide this path in your specification POST requests to
http://MYHOST/hello_world
, it will be handled by the function
hello_world
in the myapp.api
module. Optionally, you can include
x-swagger-router-controller
in your operation definition, making
operationId
relative:
paths:
/hello_world:
post:
x-swagger-router-controller: myapp.api
operationId: hello_world
Keep in mind that Connexion follows how HTTP methods work in Flask and therefore HEAD requests will be handled by the operationId
specified under GET in the specification. If both methods are supported, connexion.request.method
can be used to determine which request was made.
To customize this behavior, Connexion can use alternative
Resolvers
--for example, RestyResolver
. The RestyResolver
will compose an operationId
based on the path and HTTP method of
the endpoints in your specification:
from connexion.resolver import RestyResolver
app = connexion.App(__name__)
app.add_api('swagger.yaml', resolver=RestyResolver('api'))
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
RestyResolver
will give precedence to any operationId
encountered in the specification. It will also respect
x-router-controller
. You can import and extend connexion.resolver.Resolver
to implement your own operationId
(and function) resolution algorithm.
Connexion automatically maps the parameters defined in your endpoint specification to arguments of your Python views as named parameters, and, whenever possible, with value casting. Simply define the endpoint's parameters with the same names as your views arguments.
As an example, say you have an endpoint specified as:
paths:
/foo:
get:
operationId: api.foo_get
parameters:
- name: message
description: Some message.
in: query
type: string
required: true
And the view function:
# api.py file
def foo_get(message):
# do something
return 'You send the message: {}'.format(message), 200
In this example, Connexion automatically recognizes that your view
function expects an argument named message
and assigns the value
of the endpoint parameter message
to your view function.
Warning
When you define a parameter at your endpoint as not required, and
this argument does not have default value in your Python view, you will get
a "missing positional argument" exception whenever you call this endpoint
WITHOUT the parameter. Provide a default value for a named argument or use
**kwargs
dict.
Whenever possible, Connexion will try to parse your argument values and do type casting to related Python native values. The current available type castings are:
Swagger Type | Python Type |
---|---|
integer | int |
string | str |
number | float |
boolean | bool |
array | list |
object | dict |
If you use the array
type In the Swagger definition, you can define the
collectionFormat
so that it won't be recognized. Connexion currently
supports collection formats "pipes" and "csv". The default format is "csv".
Connexion is opinionated about how the URI is parsed for array
types.
The default behavior for query parameters that have been defined multiple
times is to join them all together. For example, if you provide a URI with
the the query string ?letters=a,b,c&letters=d,e,f
, connexion will set
letters = ['a', 'b', 'c', 'd', 'e', 'f']
.
You can override this behavior by specifying the URI parser in the app or api options.
from connexion.decorators.uri_parsing import Swagger2URIParser
options = {'uri_parsing_class': Swagger2URIParser}
app = connexion.App(__name__, specification_dir='swagger/', options=options)
You can implement your own URI parsing behavior by inheriting from
connextion.decorators.uri_parsing.AbstractURIParser
.
There are three URI parsers included with connection. 1. AlwaysMultiURIParser (default)
This parser is backwards compatible, and joins together multiple instances of the same query parameter.
- Swagger2URIParser
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 tomulti
. 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
, andcollectionFormat: csv
, then connexion will setletters = ['d', 'e', 'f']
- FirstValueURIParser
This parser behaves like the Swagger2URIParser, except that it prefers the
first defined value. For example, if you provided a URI with the query
string
?letters=a,b,c&letters=d,e,f
andcollectionFormat: csv
then connexion will setletters = ['a', 'b', 'c']
Connexion can apply strict parameter validation for query and form data parameters. When this is enabled, requests that include parameters not defined in the swagger spec return a 400 error. You can enable it when adding the API to your application:
app.add_api('my_apy.yaml', strict_validation=True)
You can also define a basePath
on the top level of the API
specification. This is useful for versioned APIs. To serve the
previous endpoint from http://MYHOST/1.0/hello_world
, type:
basePath: /1.0
paths:
/hello_world:
post:
operationId: myapp.api.hello_world
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.add_api('my_api.yaml', base_path='/1.0')
Connexion makes the OpenAPI/Swagger specification in JSON format
available from swagger.json
in the base path of the API.
You can disable the Swagger JSON at the application level:
app = connexion.App(__name__, specification_dir='swagger/',
swagger_json=False)
app.add_api('my_api.yaml')
You can also disable it at the API level:
app = connexion.App(__name__, specification_dir='swagger/')
app.add_api('my_api.yaml', swagger_json=False)
When specifying HTTPS as the scheme in the API YAML file, all the URIs in the served Swagger UI are HTTPS endpoints. The problem: The default server that runs is a "normal" HTTP server. This means that the Swagger UI cannot be used to play with the API. What is the correct way to start a HTTPS server when using Connexion?
One way, described by Flask, looks like this:
from OpenSSL import SSL
context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file('yourserver.key')
context.use_certificate_file('yourserver.crt')
app.run(host='127.0.0.1', port='12344',
debug=False/True, ssl_context=context)
However, Connexion doesn't provide an ssl_context parameter. This is
because Flask doesn't, either--but it uses **kwargs
to send the
parameters to the underlying werkzeug server.
The Swagger UI for an API is available, by default, in
{base_path}/ui/
where base_path
is the base path of the API.
You can disable the Swagger UI at the application level:
app = connexion.App(__name__, specification_dir='swagger/',
swagger_ui=False)
app.add_api('my_api.yaml')
You can also disable it at the API level:
app = connexion.App(__name__, specification_dir='swagger/')
app.add_api('my_api.yaml', swagger_ui=False)
If necessary, you can explicitly specify the path to the directory with swagger-ui to not use the connexion-embedded swagger-ui distro. In order to do this, you should specify the following option:
options = {'swagger_path': '/path/to/swagger_ui/'}
app = connexion.App(__name__, specification_dir='swagger/', options=options)
Make sure that swagger_ui/index.html
loads by default local swagger json.
You can use the api_url
jinja variable for this purpose:
const ui = SwaggerUIBundle({ url: "{{ api_url }}/swagger.json"})
Connexion uses the default Flask server. For asynchronous
applications, you can also use Tornado as the HTTP server. To do
this, set your server to tornado
:
import connexion
app = connexion.App(__name__, specification_dir='swagger/')
app.run(server='tornado', port=8080)
You can use the Flask WSGI app with any WSGI container, e.g. using Flask with uWSGI (this is common):
app = connexion.App(__name__, specification_dir='swagger/')
application = app.app # expose global WSGI application object
You can use the aiohttp
framework as server backend as well:
import connexion
app = connexion.AioHttpApp(__name__, specification_dir='swagger/')
app.run(port=8080)
Note
Also check aiohttp handler examples.
Set up and run the installation code:
$ sudo pip3 install uwsgi
$ uwsgi --http :8080 -w app -p 16 # use 16 worker processes
See the uWSGI documentation for more information.
Additional information is available at Connexion's Documentation Page.
A full changelog is maintained on the GitHub releases page.
We welcome your ideas, issues, and pull requests. Just follow the usual/standard GitHub practices.
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 (Zalando SE, Berlin) shall be under the terms and conditions of Apache License 2.0 written below, without any additional copyright information, terms or conditions.
If you'd like to become a more consistent contributor to Connexion, we'd love your help working on these we have a list of issues where we are looking for contributions.
We'd like to thank all of Connexion's contributors for working on this project, and to Swagger/OpenAPI for their support.
Copyright 2015 Zalando SE
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.