Flask

Enhanced Advanced

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 command and rsconnect-python package.

rsconnect deploy api -n myServer MyApiPath/

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 need to specify a different entrypoint so that Posit 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 Connect automatically integrates to provide a web-accessible documentation or API console interface. When using any of the following packages, ensure you 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)}

if __name__ == '__main__':
    app.run(debug=True)
# flask-restx-example/requirements.txt
Flask-RESTX

Use rsconnect to deploy this example by specifying the flask-restx-example directory containing app.py, greetings.json, 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)}

if __name__ == '__main__':
    app.run(debug=True)
# flask-api-example/requirements.txt
Flask-API

Use rsconnect to deploy this example by specifying the flask-api-example directory containing app.py, greetings.json 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)})

if __name__ == '__main__':
    app.run(debug=True)
# flask-flasgger-example/requirements.txt
Flasgger

Use rsconnect to deploy this example by specifying the flask-flasgger-example directory containing app.py, greetings.json 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}."}

if __name__ == '__main__':
    app.run(debug=True)

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.