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.