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 that 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
.
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 groups of the current logged in user by
parsing the RStudio-Connect-Credentials
request header.
Most environments have unique usernames where each
user
identifies a single user andgroups
the name of the groups the user is a member of. In large organizations with hundreds of users, it is possible to have two users with, for example, the same last name and initials, which could result in a duplicated username. If you're expecting such a large number of logged in users to access your Flask API, please be sure to consult your administrator to confirm whether this condition is possible in your environment. Note thatgroups
may suffer the same issue of duplicates names when users are authenticating against some providers, Azure in particular. If you want to ensure uniqueness of the user or group identifiers RStudio Connect should be configured with the optionAuthorization.ContentCredentialsUseGUID
. When enabled, your content will receive the user and groups GUIDs instead of their names inuser
andgroups
. Not only are these values guaranteed to be unique but they can also be used to query for additional information about the user and groups using the Connect Server API.
Group meta-data is populated when using most authentication providers with the exception of LDAP.
Your Flask API should access the
RStudio-Connect-Credentials
header value via theflask.request
object'sheaders
property. This value is populated from theHTTP_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}."}