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
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
= Flask(__name__)
app = Api(
api ="0.1.0", title="Greetings API", description="A friendly API for humans"
app, version
)= json.load(open("greetings.json"))
db = api.namespace("greetings", description="Greeting operations")
ns = api.model(
greeting "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__':
=True) app.run(debug
# 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.
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
= FlaskAPI(__name__)
app = json.load(open("greetings.json"))
db
@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__':
=True) app.run(debug
# 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
= Flask(__name__)
app = LazyJSONEncoder
app.json_encoder "SWAGGER"] = {"title": "Greetings API"}
app.config[= json.load(open("greetings.json"))
db = 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__':
=True) app.run(debug
# 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.
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
= Flask(__name__)
app
def get_credentials(req):
"""
Returns a dict containing "user" and "groups" information populated by
the incoming request header "RStudio-Connect-Credentials".
"""
= req.headers.get("RStudio-Connect-Credentials")
credential_header if not credential_header:
return {}
return json.loads(credential_header)
@app.route("/hello")
def hello():
= get_credentials(request)
user_metadata = user_metadata.get("user")
username if username is None:
return {"message": "Howdy, stranger."}
return {"message": f"So nice to see you, {username}."}
if __name__ == '__main__':
=True) app.run(debug
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.