Package: fastapitableau
FastAPI TableauDocumentation:https://rstudio.github.io/fastapitableauSource Code:https://github.com/rstudio/fastapitableauFastAPI Tableau lets you call external Python code from Tableau workbooks viaTableau Analytics
Extensions. To do this, you write an API usingFastAPI, with some minor modifications. If you aren't already familiar with FastAPI, we suggest you start with theirtutorial.The main change required for your API to be callable from Tableau is to replace theFastAPIapp class withFastAPITableau.# Base FastAPIfromfastapiimportFastAPIapp=FastAPI()# FastAPI TableaufromfastapitableauimportFastAPITableauapp=FastAPITableau()When you do this, FastAPI Tableau will reroute and transform requests from Tableau, which arrive at the/evaluateendpoint, to the endpoints you define in your app. It will also show documentation for Tableau users at the root of your API, with copy-and-paste code samples for Tableau calls.Before you write an extension with FastAPI Tableau, you should be familiar withFastAPI. TheTutorialis a great place to start.You should also take a look at the limitations of FastAPI Tableau extensions, describedbelow.A simple FastAPI Tableau extensionfromtypingimportListfromfastapitableauimportFastAPITableauapp=FastAPITableau(title="Simple Example",description="A *simple* example FastAPITableau app.",version="0.1.0",)@app.post("/capitalize")defcapitalize(text:List[str])->List[str]:capitalized=[t.upper()fortintext]returncapitalizedIf you put this code in a file namedsimple.pyin your terminal's working directory, you can serve it locally usingUvicorn, by invokinguvicorn simple:app --reload.FastAPI Tableau extensions support a limited set of data model featuresFastAPI Tableau extensions have a few constraints compared to standard FastAPI apps. Some of these are due to the ways that Tableau sends data. We'll describe known limitations here, and how to work within them.All parameters that receive data from Tableau must belist fields.Tableau sends its data in JSON lists in the request body, which FastAPI converts to equivalent Python types. Compatible types areList[str],List[float],List[int], andList[bool]. You can define these parameters as arguments to an endpoint function, or as fields in a Pydantic model.Your endpoints must also return data in a compatible list types, and the data returned must be the same length as the inputs received.This requirement is not enforced by FastAPI Tableau, but Tableau maps the data sent back by an extension into a table column.You can use query parameters in your functions by declaring singular (non-List) arguments.FastAPI assumes that anybool,float,int, orstrvariables are query parameters. You can use these in thescriptargument when calling the API from Tableau.Putting this all together, you can have as many list and singular parameters as you want, declared in the function definition. List parameters will come from Tableau data objects, and singular parameters can be set in the Tableau calculation.For example, if you wanted an endpoint to multiply data by a user-provided number, you could define it like this:@app.post("/multiply")defmultiply(numbers:List[float],multiplier:float)->List[float]:result=[i*multiplierforiinnumbers]returnresultFastAPI Tableau will automatically generate Tableau usage examples for your endpoints, using your type annotations, so providing complete type annotations will make it easier to use your APIs from Tableau.Additional descriptive metadata for endpointsYou can add additional metadata to endpoints as you would with a regular FastAPI app, in the endpoint decorator and the docstring.@app.post("/paste",summary="Parallel concatenation on two lists of strings",response_description="A list of concatenated strings",)defpaste(first:List[str],second:List[str])->List[str]:"""Given two lists of strings, iterate over them, concatenating parallel items.- **first**: the first list of strings- **second**: the second list of strings"""result=[a+" "+bfora,binzip(first,second)]returnresultThis metadata will appear in the documentation for Tableau users.Deploying extensions to RStudio ConnectYou can deploy FastAPI Tableau extensions to RStudio Connect withrsconnect-python. Detailed documentation can be foundover there.rsconnectdeployfastapi\--serverhttps://connect.example.org:3939\--api-keymy-api-key\my_api_directory/rsconnect-python assumes that your API is the only Python in its directory. For best results, specify your API's dependencies in arequirements.txtfile in the same directory. See more informationhere.Using self-signed certificatesWhen using self-signed certificates with FastAPI Tableau, you must make Python aware of the path to the certificate.Set theREQUESTS_CA_BUNDLEenvironment variable to the path to your certificate file. This variable is used by the underlying Requests library, and is documentedhere.In RStudio Connect, set the environment variablein the Vars tab of the Content Settings Panel. Note that applications running on RStudio Connect cannot access the/etcdirectory, so the certificate must be in a different location, such as/opt/python.Calling an extension endpoint in TableauYou can copy and paste the usage example (theSCRIPT_*command) into a calculated field in Tableau (it generates the correct URL), and replace the argument placeholders with actual values from the Tableau workbook.Working with Tableau dataWe've found that a few practices in Tableau ensure that the data you pass to a Tableau extension is sent correctly.You must turn off "Aggregate Measures" under the "Analysis" menu for Tableau to pass the correct values to the extension. If this setting is on, Tableau will send aggregated data to the extension, which may cause inaccuracies in computations.With this value off, calculated fields don't allow you to pass raw values directly to an extension. Those values must be wrapped in an aggregating function. Since we've turned "Aggregate Measures" off, these functions won't actually aggregate the data. We've had success usingATTR([VALUE_NAME]).Debug loggingTo enable verbose debug logging, set the environment variableFASTAPITABLEAU_LOG_LEVELtoDEBUG.