Code Libraries
Python library
Updated: June 19, 2025
Prerequisites
- Python 3.6+
Clone the repository
Clone FireTail's Python quickstart GitHub repo:
git clone https://github.com/FireTail-io/python-openapi-quickstarts.git
Install FireTail
Run the following command in your command line:
$ pip install firetail
Usage
Running FireTail
Place your API YAML inside a folder in the root path of your application (e.g., swagger/
). Then run:
import firetail
app = firetail.App(__name__, specification_dir='swagger/')
app.add_api('my_api.yaml')
app.run(port=8080)
OAuth 2 Authentication and Authorization
FireTail supports one of three OAuth 2 handling methods. In the API security definition, you must include either 'x-tokenInfoUrl' or 'x-tokenInfoFunc' (or set the TOKENINFO_UR
L or TOKENINFO_FUNC
environment variables, respectively). 'x-tokenInfoUrl' should contain a URL to validate and retrieve the token information, while 'x-tokenInfoFunc' should reference a function used to obtain the token information. If both 'x-tokenInfoUrl' and 'x-tokenInfoFunc' are provided, FireTail will prioritize using the function method. FireTail expects to receive the OAuth token in the Authorization header field, following the format described in rfc6750, section 2.1. This approach significantly differs from the usual OAuth flow.
Dynamic Rendering of Your Specification
FireTail uses Jinja2 to allow specification parameterization through the arguments
parameter. You can define specification arguments for the application either globally (using the firetail.App
constructor) or for each specific API (using the firetail ion.App#add_api`` method):
app = firetail.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.
Endpoint Routing to Your Python Views
FireTail 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 `` https://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
(or x-openapi-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 FireTail adheres to the HTTP methods work in Flask. Consequently, HEAD requests will be managed by the operationId specified under GET in the specification. If both methods are supported, you can use firetail.request.method
to determine which type of request was made.
Automatic Routing
To customize this behavior, FireTail allows the use of alternative Resolvers
, such as RestyResolver
. The RestyResolver
generates an operationId
based on the path and HTTP method of the endpoints in your specification:
from firetail.resolver import RestyResolver
app = firetail.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 firetail.resolver.Resolver
to implement your own operationId
(and function) resolution algorithm.
Automatic Parameter Handling
FireTail automatically maps the parameters defined in your endpoint specification to the named parameters of your Python views, and performs value casting whenever possible. To achieve this, ensure the endpoint's parameters have the same names as your view arguments.
For example, if you have an endpoint specified as follows:
paths:
/foo:
get:
operationId: api.foo_get
parameters:
- name: message
description: Some message.
in: query
type: string
required: true
And the view function:
paths:
# api.py file
def foo_get(message):
return 'You sent the message: {}'.format(message), 200
In this example, FireTail automatically recognizes that your view function expects an argument named message
and assigns the value of the endpoint parameter message
to your view function.
Note:
In the OpenAPI 3.x.x specification, the requestBody
does not have a name. By default, it will be passed in as body
. You can optionally provide the x-body-name
parameter in your requestBody
(or in the legacy position within the requestBody
schema) to override the name of the parameter that will be passed to your handler function.
/path:
post:
requestBody:
x-body-name: body
content:
application/json:
schema:
# Legacy location here should be ignored because
the preferred location for x-body-name
is at the requestBody level above
x-body-name: this_should_be_ignored
$ref: '#/components/schemas/someComponent'
Warning:
When you define a parameter at your endpoint as not required, and this argument does not have a default value in your Python view, you will encounter a "missing positional argument" exception whenever you call this endpoint without the parameter. To avoid this, provide a default value for the named argument or use the **kwargs
dictionary.
Type Casting
FireTail attempts to parse your argument values and do type casting to related Python native values. Supported type castings are:
- integer:
int
- string:
str
- number:
float
- boolean:
bool
- array:
list
- null:
None
- object:
dict
If you use the array type in the Swagger definition, you can define the collectionFormat
so that it will be recognized correctly. FireTail currently supports the "pipes" and "csv" collection formats, with "csv" as the default.
FireTail is opinionated about how the URI is parsed for array types. By default, for query parameters defined multiple times, FireTail uses the right-most value. For example, if you provide a URI with the query string ?letters=a,b,c&letters=d,e,f
, FireTail will set letters = ['d', 'e', 'f']
.
You can override this behavior by specifying the URI parser in the app or API options.
from firetail.decorators.uri_parsing import AlwaysMultiURIParser
options = {'uri_parser_class': AlwaysMultiURIParser}
app = firetail.App(__name__, specification_dir='swagger/', options=options)
You can also implement your own URI parsing behavior by inheriting from firetail.decorators.uri_parsing.AbstractURIParser
.
FireTail includes several URI parsers by default
.tg {border-collapse:collapse;border-spacing:0;margin:0px auto;} .tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px; overflow:hidden;padding:10px 5px;word-break:normal;} .tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px; font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;} .tg .tg-hx86{background-color:#FFF;color:#1F2328;text-align:left;vertical-align:top}
OpenAPIURIParser default: OpenAPI 3.0
This parser adheres to the OpenAPI 3.x.x spec, and uses the style parameter.
Query parameters are parsed from left to right, so if a query parameter is defined twice, then the right-most definition will take precedence.
For example, if you provided a URI with the query string ?letters=a,b,c&letters=d,e,f, and style: simple, then FireTail will set letters = ['d', 'e', 'f'].
For additional information see OpenAPI 3.0 Style Values.
Swagger2URIParser default: OpenAPI 2.0
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 FireTail will set letters = ['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 and collectionFormat: csv then FireTail will set letters = ['a', 'b', 'c']
AlwaysMultiURIParser
This parser is backwards compatible with FireTail 1.x. It joins toge
Parameter Validation
Enable strict parameter validation to ensure that requests include only the parameters defined in the swagger spec.
app.add_api('my_apy.yaml', strict_validation=True)
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 `` https://MYHOST/1.0/hello_world``.
If you are using OpenAPI 3.x.x, 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.
servers:
- url: https://MYHOST/1.0
description: full url example
- url: /1.0
description: relative path example
paths:
...
If you are using OpenAPI 2.0, you can define a basePath
on the top level of your OpenAPI 2.0 specification.
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.add_api('my_api.yaml', base_path='/1.0')
Swagger JSON
FireTail makes the OpenAPI/Swagger specification in JSON format available from either swagger.json
(for OpenAPI 2.0) or openapi.json
(for OpenAPI 3.x.x) at the base path of the API. For example, if your base path was 1.0
, then your spec would be available at /1.0/openapi.json
.
You can disable serving the spec JSON at the application level:
options = {"serve_spec": False}
app = firetail.App(__name__, specification_dir='openapi/',
options=options)
app.add_api('my_api.yaml')
You can also disable it at the API level:
options = {"serve_spec": False}
app = firetail.App(__name__, specification_dir='openapi/')
app.add_api('my_api.yaml', options=options)
HTTPS Support
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 FireTail?
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, FireTail 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.
Swagger UI Console
The Swagger UI for an API is available through pip extras. You can install it with pip install firetail[swagger-ui]
. It will be served up at {base_path}/ui/
where base_path
is the base path of the API.
You can disable the Swagger UI at the application level:
app = firetail.App(__name__, specification_dir='openapi/',
options={"swagger_ui": False})
app.add_api('my_api.yaml')
You can also disable it at the API level:
app = firetail.App(__name__, specification_dir='openapi/')
app.add_api('my_api.yaml', options={"swagger_ui": False})
If you wish to provide your own swagger-ui distro, note that FireTail expects a jinja2 file called swagger_ui/index.j2
in order to load the correct swagger.json
by default. Your index.j2
file can use the openapi_spec_url
jinja variable for this purpose: const ui = SwaggerUIBundle({ url: ""})
Additionally, if you wish to use swagger-ui-3.x.x, it is also provided by installing firetail[swagger-ui], and can be enabled like this:
from swagger_ui_bundle import swagger_ui_3_path
options = {'swagger_path': swagger_ui_3_path}
app = firetail.App(__name__, specification_dir='swagger/', options=options)
Server Backend
UBy default FireTail uses the Flask server. For asynchronous applications, you can also use Tornado as the HTTP server. To do this, set your server to tornado
:
import firetail
app = firetail.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 = firetail.App(__name__, specification_dir='swagger/')
application = app.app # expose global WSGI application object
Set up and run the installation code:
$ sudo pip3 install uwsgi
$ uwsgi --http :8080 -w app -p 16 # use 16 worker processes