Flask#
Flask is a lightweight Python WSGI web application framework that is widely used for creating APIs.
Deploying#
Flask APIs can be deployed with the
rsconnect-python
package. See the Publishing with
rsconnect-python section for
details.
When deploying a Flask API, ensure that you specify the correct entrypoint
for the specific app you are deploying.
The example applications in this section have their source code in a file named app.py
,
and within that file, the Flask application object is named app
. So the entrypoint
specified here is app:app
. If the main source file or application object is named
differently, you will need to specify a different entrypoint so that RStudio Connect
can locate the application object to serve. See the
documentation on entrypoints
for more information.
Examples#
There are many packages available to help improve your Flask API. Among them, there are a few with which RStudio Connect will automatically integrate to provide a web-accessible documentation or API console interface. When using any of the following packages, please ensure to follow the respective package maintainers' guidance, especially regarding version usage and security considerations.
Requirements#
The example applications shown below for each package are read-only APIs for
listing and fetching greetings for a small number of languages by locale name,
where the db
is populated from a greetings.json
file with this format:
{
"ar": "آلو",
"bn": "হ্যালো",
"chr": "ᏏᏲ",
"en": "Hello",
"es": "Hola",
"sw": "هَبَارِ",
"zh": "你好"
}
You must create and include this greetings.json
in each of the examples
below.
Flask-RESTX#
The Flask-RESTX package is an extension for Flask that adds support for quickly building REST APIs. Flask-RESTX encourages best practices with minimal setup.
If you are familiar with Flask, Flask-RESTX should be easy to pick up. It provides a coherent collection of decorators and tools to describe your API and expose its documentation properly using Swagger.
The default behavior when using the Swagger
documentation
support in Flask-RESTX is to provide a Swagger UI console at GET /
.
A simplified Flask-RESTX example looks like this:
# flask-restx-example/app.py
# -*- coding: utf-8 -*-
import json
from flask import Flask
from flask_restx import Api, Resource, fields
app = Flask(__name__)
api = Api(
app, version="0.1.0", title="Greetings API", description="A friendly API for humans"
)
db = json.load(open("greetings.json"))
ns = api.namespace("greetings", description="Greeting operations")
greeting = api.model(
"Greeting",
{
"text": fields.String(required=True, description="Text of the Greeting"),
"lang": fields.String(required=True, description="Language of the Greeting"),
},
)
@ns.route("/")
class GreetingList(Resource):
@ns.doc("list_greetings")
@ns.marshal_list_with(greeting)
def get(self):
return [{"lang": lang, "text": text} for lang, text in sorted(db.items())]
@ns.route("/<string:lang>")
@ns.param("lang", "The language identifier")
class Greeting(Resource):
@ns.doc("get_greeting")
@ns.marshal_with(greeting)
def get(self, lang):
return {"lang": lang, "text": db.get(lang)}
# flask-restx-example/requirements.txt
Flask-RESTX
Use rsconnect
to deploy this example by specifying the flask-restx-example
directory containing both app.py
and requirements.txt
:
rsconnect deploy api -n <saved server name> --entrypoint app:app ./flask-restx-example/
Flask-API#
The Flask-API package is a drop-in replacement for Flask that provides an implementation of browsable APIs similar to what Django REST framework provides. It gives you properly content negotiated-responses and smart request parsing.
Note
Flask-API requires Python 3.
The default behavior when using Flask-API is to provide a web-accessible
API console at the location of each resource (route) based on the requested
content type (e.g. web console for Accept: text/html
, API resource for
Accept: application/json
).
A minimal Flask-API example is fairly similar to a minimal Flask example:
# flask-api-example/app.py
# -*- coding: utf-8 -*-
import json
from flask_api import FlaskAPI
app = FlaskAPI(__name__)
db = json.load(open("greetings.json"))
@app.route("/", methods=["GET"])
def greetings():
return [{"lang": lang, "text": text} for lang, text in sorted(db.items())]
@app.route("/<string:lang>", methods=["GET"])
def greeting(lang="en"):
return {"lang": lang, "text": db.get(lang)}
# flask-api-example/requirements.txt
Flask-API
Use rsconnect
to deploy this example by specifying the flask-api-example
directory containing both app.py
and requirements.txt
:
rsconnect deploy api -n <saved server name> --entrypoint app:app ./flask-api-example/
Flasgger#
The Flasgger package is a Flask extension to extract OpenAPI-Specification from all Flask views registered in your API.
The default behavior when using the support for Swagger,
OpenAPI, or Marshmallow
APISpec is to provide a Swagger UI
console at GET /apidocs
.
To serve the Swagger UI console at GET /
, update the value of specs_route
in the Flasgger config dict.
A simplified Flasgger example that uses a docstring-based specification looks like this:
# flask-flasgger-example/app.py
# -*- coding: utf-8 -*-
import json
from flask import Flask, jsonify, request
from flasgger import Swagger, LazyString, LazyJSONEncoder
app = Flask(__name__)
app.json_encoder = LazyJSONEncoder
app.config["SWAGGER"] = {"title": "Greetings API"}
db = json.load(open("greetings.json"))
swagger = Swagger(
app,
template={
"swaggerUiPrefix": LazyString(lambda: request.environ.get("SCRIPT_NAME", ""))
},
)
@app.route("/greetings/")
def list():
"""Example endpoint return all known greetings
This is using docstring for specifications
---
tags:
- greetings
operationId: list_greetings
consumes:
- application/json
produces:
- application/json
security:
greetings_auth:
- 'read:greetings'
schemes: ['http', 'https']
deprecated: false
responses:
200:
description: All known greetings
examples:
- ["Hello", "هَبَارِ"]
"""
return jsonify([{"lang": lang, "text": text} for lang, text in sorted(db.items())])
@app.route("/greetings/<lang>/")
def get(lang):
"""Example endpoint return a greeting by language
This is using docstring for specifications
---
tags:
- greetings
parameters:
- name: lang
in: path
type: string
required: true
default: en
description: A greeting in which language?
operationId: get_greetings
consumes:
- application/json
produces:
- application/json
security:
greetings_auth:
- 'read:greetings'
schemes: ['http', 'https']
deprecated: false
responses:
200:
description: The greeting for the given language
examples:
en: Hello
"""
return jsonify({"lang": lang, "text": db.get(lang)})
# flask-flasgger-example/requirements.txt
Flasgger
Use rsconnect
to deploy this example by specifying the
flask-flasgger-example
directory containing both app.py
and
requirements.txt
:
rsconnect deploy api -n <saved server name> --entrypoint app:app ./flask-flasgger-example/
User meta-data#
Flask APIs can access the username and the names of the groups of the current
logged in user by parsing the RStudio-Connect-Credentials
request header.
Note
Your Flask API should access the RStudio-Connect-Credentials
header value via the flask.request
object's headers
property. This value is populated from the HTTP_RSTUDIO_CONNECT_CREDENTIALS
environment variable present in the underlying WSGI environ.
This simple Flask API defines a /hello
route that greets the arriving user.
# -*- coding: utf-8 -*-
import json
from flask import Flask, request
app = Flask(__name__)
def get_credentials(req):
"""
Returns a dict containing "user" and "groups" information populated by
the incoming request header "RStudio-Connect-Credentials".
"""
credential_header = req.headers.get("RStudio-Connect-Credentials")
if not credential_header:
return {}
return json.loads(credential_header)
@app.route("/hello")
def hello():
user_metadata = get_credentials(request)
username = user_metadata.get("user")
if username is None:
return {"message": "Howdy, stranger."}
return {"message": f"So nice to see you, {username}."}
User and Group uniqueness#
Most environments have unique usernames where each user
identifies a single
user and groups
the name of the groups the user is a member of.
However, in large organizations with hundreds of users and groups, this may not be true. See the Admin Guide sections Credentials for Content for more information.