Skip to content

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.