all sorts of data available.
Specifically, we’re going to build our API using “serverless functions”, which allow us to upload individual Python functions into the cloud and run them when someone goes to our API’s web URL. The value returned by our code will be what the page loads on the visitor’s computer. This has a few benefits for us:
This guide assumes that you’ve completed the VM Workstation tutorial and NoSQL. If you haven’t, go back and skim through those now. Using a remote VM is not required to do any of the things outlined in this tutorial, but it will make it easier for course staff to help you debug things. If you were unable to complete the NoSQL tutorial, you can use the databases that your classmates or course staff made; just ask them for the appropriate URI and access key (this tutorial will remind you when you get to that point).
We’ll start by making sure our workstation VM is turned on. Open a web browser and log in to the Azure web portal.
Go to the virtual machines dashboard by using the search bar at the top:
Select your workstation VM from the list, and if it’s stopped, click the Start
button:
Keep this window open in the background, we’ll be using it again in a minute.
Open VSCode and make sure your window is remotely connected to your cloud virtual machine. If it is, you’ll see the VM’s public IP address in the bottom left blue box:
If not, click the blue ><
button and open an SSH connection to the workstation as shown in the workstation tutorial.
โ Having trouble with your VM? โ
If you can’t connect VSCode to your VM, check out these troubleshooting guides from the Workstation tutorial:
Azure provides us with an application called “Azure Function Core Tools” that we can install on our workstation VM (or personal computer) to test out our API code before actually publishing it. The core tools will let us invoke our Python code from a web browser, but using a private URL that only browsers on our workstation VM (or personal computer) can access.
To install Azure Function Core Tools, start by opening a new terminal in the remote VSCode window:
We’ll start by installing the “Azure CLI,” which provides some useful commands in our terminal for interacting with Azure. Run this command:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
It should output a bunch of junk. If it was successful, you should be able to run this command:
az
and see output like this:
Now we’ll install the Function Core Tools. Run these commands to install Microsoft’s code signing key to your VM. This helps us confirm that any Microsoft-provided tools we install are free of viruses or malware:
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
The first command should complete without errors, and the second shouldn’t leave any output at all:
Now, run this command to let the apt
tool, which we use to install software, know about Microsoft’s software. The command should complete without output:
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs 2>/dev/null)-prod $(lsb_release -cs 2>/dev/null) main" > /etc/apt/sources.list.d/dotnetdev.list'
Finally, run these two commands to install the core tools:
sudo apt-get update
sudo apt-get install -y azure-functions-core-tools-4
To confirm it was successful, you should be able to run the core tools with the func
command and see a version number get printed out (it may vary from the screenshot below):
func --version
For future reference, the full documentation for how to install these tools on your own computer is here.
Let’s make a new folder to keep our function code in. Make a directory db-api
and move into it with these terminal commands:
mkdir ~/db-api
cd ~/db-api
Then, use the core tools to create a new Python project:
func init --worker-runtime python
On success, we’ll see it creates a bunch of files:
Let’s open this folder in VSCode now using the command:
code .
This should let us see all the project files in the left sidebar:
Now let’s create a Python “venv” to do our development in. In a new terminal
python3 -m venv app-env
And now run this command to enter it:
source app-env/bin/activate
You should see the environment name appear before the regular stuff in the terminal prompt:
As a reminder from our dance with “venvs” in the NoSQL tutorial, you’ll only need to create the environment once, but you might need to “activate” it every time you open a new terminal. If you’re not sure, look for that (app-env)
text before the rest of the command prompt. If you don’t see it, run that activate
command above.
Our Python code is ultimately going to be stored in function_app.py
. Double-click it in the file bar on the left to open it.
Right now, it does nothing:
Let’s add a function that will run when users visit a page called test
on our website. To do so we’ll define a Python function called test
, and annotate it with a line that defines it as the route for the URL test
.
Paste this at the bottom of the file:
That @app.route
line is what connects web browsers visiting http://www.example.com/api/test
to the Python function test
. The logging.info
line prints a message to our private terminal window, but the message won’t be seen by visitors to the web URL. The only thing the web visitor will see is the text “Hey Galaxy” (the result of the return
statement), and the status code 200
, which in web world represents “success” ๐๐. For a list of all the status codes, and what they mean, look here.
Now in terminal, let’s start the server with this command:
func start
As long as this command is running, we should be able to visit the URLs it provides. Try opening the ’test’ route in a web browser:
When you’re done testing, go back to the terminal in VSCode and hit Control + C
to turn off the test server. You’ll see a red error message, which is fine:
Let’s make publish our function to the cloud so anyone can run it.
On the Azure portal, open the Function Apps dashboard by searching for Function Apps
at the top:
From this dashboard, click + Create
:
We’ll be presented with several different “hosting plans,” which trade off things like how many people can be accessing our data at once and how fast those interactions happen. Our API is going to be tragically unpopular, so let’s select the cheapest option, Consumption
(1), and then continue with the Select
button (2):
On the wizard page select the following options:
______-atomic-portal
, where the blank _______
is replaced with your UW NetID.Then, at the bottom, click the blue Review + Create
button, and then on the confirmation page, the blue Create
button.
You’ll be brought to a deployment page which, after a few minutes, should present you with a Go to resource
button. Click that:
Hooray! We’ve set up our function app. Once we upload our code to it, we’ll be able to run that code by following opening the circled URL in a web browser:
But for now, that should look like this:
Now we have to upload our actual code to it..
In VSCode terminal, log in by running this command and following the instructions it presents:
az login
You may be asked to select the subscription you want to log into. Type the number in the list that corresponds to the Azure subscription your course staff tell you to use:
When it finishes, try running this command:
az account show
If you’ve successfully logged in, you should see some garbage spat out to the terminal including your UW NetID:
If you don’t see the above, contact your course staff and make sure you can log in through the CLI before proceeding.
Now, publish your code by running the following command, where the blank ________
is replaced with the name of the function app you created in the web portal:
func azure functionapp publish ________
After a few minutes, if the process succeeds it should present you with a URL you can open in a web browser to run your function:
Trying that out in a web browser should lead to our Hey Galaxy
page!
….Except that it doesn’t work. What happened?
If we were to use more advanced tools to open that web URL, we’d find we were getting an error called 401 Unauthorized
. It turns out, by default, Azure function apps need users to be logged in or have access keys to use the functions we publish. This is because folks often use Azure functions to work with sensitive or private data, and want to make sure only authorized users are running their code.
But we don’t care about that! We just want to allow the world to read our periodic table database. To do so, we have to change our function to allow anonymous access.
We can do this by changing the line of code that starts with @app.route
. Change this:
@app.route(route="test")
to this:
@app.route(route="test", auth_level=func.AuthLevel.ANONYMOUS)
Test your function out with func start
, and then once you confirm it still works, re-publish it with func azure functionapp publish ________
(blank replaced appropriately).
When the command completes, you should be able to run your code on the cloud by opening the invoke URL provided by the publish command:
Your friends should be able to as well. Ask them to try it out!
Now we’ll add another function to our API that allows users to perform element lookups from our NoSQL database.
First, let’s open up requirements.txt
which, as in the NoSQL tutorial, lists the packages required for our code to run. Add the following two lines to the bottom, and save the file:
azure-core
azure-cosmos
In the terminal, make sure we’re in the db-api
folder and that our virtual environment is activated. Then, run the following command:
python3 -m pip install -r requirements.txt
Now return to function_app.py
. Add this to the top of the file, right after the import logging
line:
|
|
And at the bottom of the file, let’s add the following functions:
|
|
Serverless functions are able to receive input data through “URL parameters”, similar to how normal Python functions have input parameters after their parantheses (eg, the “hello” in print("hello")
). That said, they’re structured a little differently. The person running the code, by visiting the API’s URL in a web browser, needs to provide values by putting them at the end of the URL, in this format:
http://example.com/api/route?name1=value1&name2=value2&name3=value3...
These values are called an argument list. The argument list always goes at the end of the URL, and starts with a ?
. After that, the input parameter’s name, an =
sign, and then input data itself. If there are further values that need to be provided, they’re chained together with &
s.
On the Python side, these values can be found inside the function’s req
input. We can see the code retrieving the value of a parameter called name
with the code req.params.get('name')
.
So, to look up the element Carbon, you would type out a URL like this:
http://__________/api/lookup?name=Carbon
Where the blank ______
is replaced with the proper begnning of your function app’s URL.
But we’re not ready to test it just yet. We need to give our code the secret access keys to the database.
Open up local.settings.json
, which should look something like this:
The settings contained between the curly braces in "Values": {...}
are accessible in the Python code through those os.environ
lines of code. Noting in our code that we grab the database’s URI through a setting named ACCCOUNT_HOST
, and key through a setting named ACCOUNT_KEY
, we have to add those here.
Create two lines right after the opening curly brace {
of “Values” (line 3 in the example above). On those lines, write:
"ACCOUNT_HOST": "______",
"ACCOUNT_KEY": "______",
Now head over to the Azure web portal, and open up the Cosmos DB dashboard using the search box at the top. Select your database from the list:
From there, select Keys
in the left menu (1). Click the copy button next to the URI box (2) and paste it into local.settings.json
as the ACCOUNT_HOST
value, instead of the blank ______
. Click the eye icon to show the database’s primary key (3), and then copy that too (4). Paste it into local.settings.json
as the ACCOUNT_KEY
value.
Save local.settings.json
, and then test out your function by running the terminal command func start
. We should be presented with two URLs now, each corresponding to one of the python functions in function_app.py
. We want to try the ’lookup’ one:
Open that in a web browser:
If you see the above, that’s a great sign! Now let’s look up a specific element. Try adding ?name=Carbon
to the end of the URL. If it works, you should see information like this:
That means it works! Hurray! The exact formatting/coloring of the information will likely look different on your computer, but if the information is there, that’s what we care about. If you don’t see information about carbon in your browser, (a) make sure it’s capitalized, (b) look in the VSCode terminal for the red error output. This is the first information needed to debug the problem (which your classmates and course staff can help you with). Your course staff can help you with the rest.
Once we get the local testing version of the API route working, push it into the cloud with this command:
func azure functionapp publish ________
(replacing the blank ______
appropriately). The process should complete and give us two URLs, but we’re not ready to use them yet. We have to add the database URL/access key to the cloud environment, the same way we did in the test environment with local.settings.json
. In your web browser, go to the function app’s dashboard:
Select Environment variables
from the Settings
menu on the left. If it’s not there, select Configuration
instead (Microsoft is currently updating the menu organization, and unfortunately some users have the old version while others have the newer one). You ultimately want to end up at a table full of application settings, which should look something like this:
On this page, add two new variables named ACCOUNT_HOST
and ACCOUNT_KEY
. Give them the values of their respective variables from local.settings.json
, but not surrounded by double-quotes "
.
When you’re done, hit the blue Apply
button at the bottom (or the Save
button at the top, whichever your web portal seems to have) and confirm that you want to restart the app to make changes. Now, open the url https://__________.azurewebsites.net/api/lookup?name=Carbon
, where the blank _______
is replaced with your app name. You should see similar output to when you tested earlier:
That means it all works! You’ve built an API that stores information in a database and retrieves it through a public web API. Congratulations!
๐ Challenges: