Here we set up our Python environment, code editor, and Azure cloud environment.
Azure Functions is an event-driven compute platform that removes the need to manage any server and platform components. This platform has adopted Python, a high-level, object-orientated, general-purpose programming language emphasizing code readability. The data science community and others often use Python.
You can now move your stateless Python scripts into Azure Functions. This move reduces server component handling so you can focus on developing componentized code.
In this article, we’ll demonstrate using Python to develop custom APIs. These APIs leverage the power of Azure Functions to provide scalable service to our users. In later articles, we’ll build a task tracker app without managing servers or databases ourselves.
We’ll use Python 3.9’s built-in features, including type hinting and async and await, and leverage the FastAPI framework to create a suite of modern APIs quickly. We'll do this work right in the extensible code editor Visual Studio Code (VS Code). The full project code is available on GitHub. Let’s get started.
Setting Up Our Environment
Before we dive into our Python code, let's configure Azure, Python, and Visual Studio Code so we have a way to develop and deploy our Functions.
First, we need an Azure account with a resource group ready where we can deploy our function. If you don’t already have an Azure account, sign up for a free account. It provides $200 of credit, as well as access to several popular services. When you create a new account, it asks for a credit card to verify transactions in case you use more resources than your allocation. However, these tutorials should only use some of your free tier allocation.
When you have signed up for an account and have made it to the portal screen, you can check out the Azure services. Let’s create a new resource group, like a file systems folder, to hold all the items we make in Azure.
To do this, click on Resource Groups and click the create button. We call the Resource group "PythonTutorial" and select the default Region, "East US."
Now that we’ve set up our Azure environment, we also need a code editor and Python development libraries. We have Python 3.10 installed and working on our computer already, but you can download the library if necessary. You should install Python and ensure it’s accessible from the command line.
We use Visual Studio Code as our code editor. Download it if you don’t already have it installed. This lightweight code editor has an extensible plugin system, allowing us to create and deploy Azure Functions and provide Python language support.
After you’ve downloaded and installed VS Code to your system, open the editor. Then, open a blank folder in the VS Code editor to keep all your code files together.
On the left menu is an icon comprising four squares that opens the extensions screen. For this tutorial, we’ll install two main extensions and their dependencies: Azure Functions, which enables us to configure and deploy Azure Functions, and Python, which adds support for IntelliSense, linting, and debugging. After we’ve enabled these two extensions, we’re ready to create our APIs.
Creating Our First Function App
To create our first Function app, we first need to ensure we have our VS Code editor open. Then, we open an empty folder on our system where we want all our code to reside — for instance, the e:\Repo\PythonTutorial folder.
On the left menu bar, we see an Azure icon similar to this:
We click on this icon, and under the Functions option, select Create New Project:
VS Code then prompts us to specify a folder to hold the project, which should default to the currently-open folder. We click on this folder, then select the language type for the Function: Python.
VS Code then scans our machine for an appropriate Python environment to use. The version should show up in this next window if you have Python all set up and ready to go. If not, manually specify your Python interpreter.
Finally, we can use a template for our project. Let’s go ahead and specify the HTTP trigger, which enables our functions to respond to HTTP commands. We call this function "HelloWorld" and set the authorization level to "Anonymous."
Anonymous functions allow anyone to trigger the function via HTTP. Function authorization requires a specific function API key and admin requires a particular admin authorization key.
Usually, your functions will have function authorization and a site behind an API management front end, but we stick to anonymous for this tutorial. If we hit F5 or click on the Run menu and Start Debugging, our function compiles and runs locally on port 7071 by default. The function also shows that it is listening for both GET and POST messages.
When we open the web address localhost:7071/api/HelloWorld
in a browser, we see the text response in the image above. We can also use a tool like Postman to send data via a POST to this API and return a custom message.
Adding the FastAPI Library
We could develop our APIs from scratch using Azure Functions and the base Python interpreter. It’s a reasonable approach many people take. However, we'll leverage the FastAPI library to speed up our development time and add a few extras.
Let's first download the FastAPI library by opening a new terminal window in VS Code (CTRL + SHIFT + `) and using the command:
pip install fastapi
To use the library, we also need an Asynchronous Server Gateway Interface (ASGI) to sit between the function and the library. Luckily, Anthony Shaw has an open-source ASGI that we can reuse here. We copy this file into the _future/azure/functions folder and import it into our function.
Once we have this ASGI set up, we need to reverse some standard Azure Functions configuration. First, we want FastAPI to handle all our routing. So, we change the way Azure Functions routes HTTP requests by putting the following code into our host.json file:
"extensions": {
"http": {
"routePrefix": ""
}
}
This addition allows our code to determine which paths we listen on, instead of the default route of /api/function_name.
Next, we need to modify our function.json file to expand the methods and add a route value. That way, the function passes everything it receives to our FastAPI app. To achieve this, we use the following code:
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [ "get", "post", "put", "patch", "delete" ],
"route": "{*route}"
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
We expanded the methods to include put
, patch
, and delete
. We also added the route value as "{*route}
." This change allows the function to pass through any path to the FastAPI library.
Writing Our FastAPI Functions
When this is all complete, we can now clean up our function and add a quick example. We remove all the code in our existing function and add the following:
import logging
import azure.functions as func
from _future.azure.functions._http_asgi import AsgiMiddleware
import fastapi
app = fastapi.FastAPI()
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
return AsgiMiddleware(app).handle(req, context)
This code performs four actions:
- It imports the logging and Azure Function libraries as per the standard function code.
- It imports our ASGI middleware.
- It imports FastAPI and configures the app variable to point to the library.
- It defines the main entry point for our code that takes the request and context, passes it to our application through the middleware, and waits for the HTTP response.
When we have this boilerplate code in place, we can now define our various functions below it. We use the simple example below to return a message when we GET
the path:
@app.get("/api/helloWorld")
async def get_helloWorld():
return {
"message": "This is a test message for Hello World"
}
This code section enables the app to listen on the path /api/helloWorld. When the app receives an asynchronous request, it returns a JSON object with the key-value pair:
"message": "This is a test message for Hello World"
If we run our function now, we see that we now have a single function listening to http://localhost:7071/{*route}.
One benefit of running FastAPI is we can access the Swagger documentation on the path /docs
. When you open http://localhost:7071/docs, you should now see your single GET
path to /api/helloWorld
. You should be able to open this path and execute it to see the resulting test message.
To add additional API functions, simply add more app path listeners. The FastAPI documentation goes into great depth with more use cases.
Deploying Our Function
Now that we have our function up and running locally, we can deploy it to our Azure instance.
First, we click on the Azure icon on the left menu and click the cloud-with-an-arrow icon to start configuring deployment.
Select the Sign in option and sign in to your Azure account. Select the subscription where you want to deploy resources, then choose the Create a new function app in Azure Advanced option.
There are seven steps when we create a function this way:
- Give our function app a unique name, like "pyfastapitutorial."
- Specify our runtime stack or the version of Python we are using, like Python 3.9.
- Specify the Resource Group to hold all our resources
- Select a location to host our resources.
- Choose a Function plan, like Consumption, which will only charge for code executions after using the free tier resources.
- Specify or create a storage account, which will hold our functions’ logs and other items.
- Specify or create an Application Insights account for log collection and metric monitoring.
When these steps are complete, our sample function application should be running under a public URL. As before, we can navigate to this URL’s /docs path to see any created APIs.
Next Steps
We now have a working FastAPI-based Python Function application built and hosted in Azure. From here, we can build out our API and add more endpoints to create a fully-fledged, asynchronous application. We can also change the API to implement Function Authorization and add an API gateway to provide a more secure service.
Next, however, we’ll create a more traditional web application using the Flash library and Azure WebApp. We still won’t have to manage any infrastructure and never need to leave our code editor. Yet, we’ll host the web application on a resource that allows more significant environment configurations. Continue to the second article of this series to learn more.
To learn more about how deploy a Python web app to App Service on Linux, check out Quickstart: Create a Python app.