HTTP APIs
Workshop

Tips for navigating the slides:
  • Press O or Escape for overview mode.
  • Visit this link for a nice printable version
  • Press the copy icon on the upper right of code blocks to copy the code

Welcome!

Classroom "rules":

  • I am here for you!
  • Every question is important
  • Help each other

Introductions

Tell us about yourself:

  • Name
  • Pronouns
  • Location
  • Programming/Web experience
  • What interests you about Python HTTP APIs?
  • What's your favorite flavor of ice cream / popsicle? 🍦

Today's topics

Bit (the raccoon) lecturing
  • (Review) HTTP
  • Building HTTP APIs
    • Flask
    • FastAPI
  • Hosting HTTP APIs on Azure

Prerequisites

Online development: Github account

Local development:

Local development (option 2):

HTTP APIs

Review: HTTP

A client sends an HTTP request:


                GET /index.html HTTP/1.1
                Host: www.example.com
                

The server sends back an HTTP response:


                HTTP/1.1 200 OK
                Content-Type: text/html; charset=UTF-8
                Content-Length: 208
                <!DOCTYPE html>
                    <html>
                    <head>
                        <title>Example Domain</title>
                    </head>
                    <body>
                    <h1>Example Domain</h1>
                    <p>This domain is to be used for illustrative examples in documents.</p>
                    </body>
                </html>
                

HTTP API

A client sends an HTTP request:


                GET /weather?zip=94530 HTTP/1.1
                Host: api.example.com
                

The server sends back an HTTP response:


                HTTP/1.1 200 OK
                Content-Type: text/json; charset=UTF-8
                Content-Length: 30
                {"temperature": 70, "wind": 5}
                

HTTP API response formats

  • JSON
    
                        {"temperature": 70, "wind": 5}
                        
  • XML
    
                        <weather-response>
                          <temperature>70</temperature>
                          <wind>5</wind>
                        </weather-response>
                        
  • RSS (XML)
  • Image

Read more: Most popular API data formats

HTTP APIs: GET vs. POST

GET: retrieve data from a server.
Often used with query parameters.


                GET /weather?zip=94530 HTTP/1.1
                Host: api.example.com
                

POST: send data to a server.
Data is often in JSON or form-encoded.


                POST /scores HTTP/1.1
                Host: api.example.com
                player=pamela&score=50
                

Exercise: Play with APIs

Try the APIs below, customizing the URLs as suggested, and share any fun findings with classmates.

  • Agify: Guess age based on name. Try it with your own name or celebrity names.
  • Reddit: Try it with the Python subreddit or your favorite subreddit.
  • Zippopotamus: Find out your latitude/longitude.
  • Sunrise/Sunset: Try it for your latitude/longitude.

Popular APIs

Some examples:

🔑 Most of the popular APIs require you to sign up for a key so that they can track your usage and limit calls based on your payment level.

Building an HTTP API
...in Python!

Bit (the raccoon) in front of a computer and Python logo

A simple API in Flask


                import json
                import random

                from flask import Flask, request

                app = Flask(__name__)

                @app.route('/v1/generate_name')
                def generate_name():
                    random_name = random.choice(["Minnie", "Margaret", "Myrtle"])
                    result = {"name": random_name}
                    return json.dumps(result)
                

👀 Demo: https://flask-simple-api-67uuyfbgx7tlq-function-app.azurewebsites.net/v1/generate_name

👩🏼‍💻 Code: https://github.com/pamelafox/simple-flask-api-azure-function/blob/main/api/flask_app.py

A parameterized API in Flask


                import json
                import random
                from flask import Flask, request

                app = Flask(__name__)

                @app.route('/v2/generate_name')
                def generate_name():
                    starts_with = request.args.get("starts_with")
                    names = ["Minnie", "Margaret", "Myrtle", "Noa", "Nadia"]
                    if starts_with:
                        names = [n for n in names if n.lower().startswith(starts_with)]
                    random_name = random.choice(names)
                    result = {"name": random_name}
                    return json.dumps(result)
                

👀 Demo: https://flask-simple-api-67uuyfbgx7tlq-function-app.azurewebsites.net/v2/generate_name

👩🏼‍💻 Code: https://github.com/pamelafox/simple-flask-api-azure-function/blob/main/api2/flask_app.py

What's that API missing?

  • Error checking
    • Parameter types
    • Required vs. optional parameters
  • Documentation

FastAPI to the rescue!

FastAPI is a Python framework designed specifically for building HTTP APIs.

  • Fast to build and fast to execute
  • Relies on Python types (via Pydantic)
  • Auto-generated documentation
  • Based on the OpenAPI specification.

A parameterized API in FastAPI


                import random

                import fastapi

                app = fastapi.FastAPI()

                @app.get("/generate_name")
                async def generate_name(starts_with: str = None):
                    names = ["Minnie", "Margaret", "Myrtle", "Noa", "Nadia"]
                    if starts_with:
                        names = [n for n in names if n.lower().startswith(starts_with)]
                    random_name = random.choice(names)
                    return {"name": random_name}
                

👀 Demo: simple-fastapi-azure-function-qdqlk63tlwxuw-function-app.azurewebsites.net/docs

👩🏼‍💻 Code: github.com/pamelafox/simple-fastapi-azure-function/blob/main/api/main.py

Running FastAPI locally

1. Put code in main.py

2. Install requirements


                pip install fastapi
                pip install "uvicorn[standard]"
                

3. Run the server


                uvicorn main:app --reload
                

4. Try the API and docs
http://127.0.0.1:8000/generate_name
http://127.0.0.1:8000/docs

Exercise: Make an API

Starting from this repo:
github.com/pamelafox/simple-fastapi-azure-function

  1. Follow the readme steps to get the FastAPI app running.
  2. Add more names to the list.
  3. Add a new API parameter, like ends_with, includes, or length.
  4. Add a new route to generate something else, like pet names.

🙋🏼‍♀️🙋🏾‍♀️🙋🏽‍♀️ Let us know if you need any help! 🙋🏻‍♀️🙋🏽‍♂️🙋🏿‍♀️

Demo: Regression Model API

A parameterized API based on a sklearn model.


                model = joblib.load(f"{os.path.dirname(os.path.realpath(__file__))}/model.pkl")

                @app.get("/model_predict")
                async def model_predict(
                    years_code: int,
                    years_code_pro: int,
                    ed_level: categories.EdLevel,
                    main_branch: categories.MainBranch,
                    country: categories.Country):
                    X_new = numpy.array([[years_code, years_code_pro, ed_level.value, main_branch.value, country.value]])
                    result = model.predict(X_new)
                    return {"prediction": result[0]}
                

👀 Demo: salary-model2-sibqf23ha7ib2-function-app.azurewebsites.net/docs

👩🏼‍💻 Code: https://github.com/pamelafox/regression-model-azure-demo/blob/main/function/model_predict/__init__.py

Hosting an HTTP API
...on Azure!

Bit (the raccoon) in the clouds next to Azure logo

Hosting considerations

  • How much traffic do you expect?
  • How variable will the traffic be?
  • Do you need scale-to-zero?
  • What's your budget?
  • Is it public facing?
  • How will you manage API use?

Azure hosting options

Cloud Azure
Environment Containers PaaS
Azure Kubernetes Service Container Management Azure App Service Serverless
Azure Container Apps Azure Functions

For FastAPI, App Service or Functions are good choices.

Hosting a FastAPI on Azure Functions

FastAPI API architecture diagram: Azure Functions, App Service Plan, Storage account, App Insights, Log Analytics Workspace

Deploying a FastAPI to Azure Functions

Using the Azure Dev CLI:

What's the hosted API missing?

  • Subscription keys
  • Quotas/Rate limiting
  • Caching
  • CORS handling
  • IP blocking

Adding API Management Policy

An Azure API Management Policy provides all the additional features of a public API.

FastAPI API architecture diagram: Azure Functions, Storage account, API Management policy

Exercise: Deploy an API

Starting from this repo (or your fork):
github.com/pamelafox/simple-fastapi-azure-function

  1. Sign up for a free Azure account
  2. Either open the project in Codespaces or follow these installation steps for the Azure Developer CLI.
  3. Run azd up. If prompted, login to your Azure account.
  4. If it deploys successfully, share the endpoint URL with your classmates. If not, let us know about any bugs. 🪲
  5. Once you've verified the app is working, run azd down to un-deploy the app (so that you don't waste cloud resources unnecessarily).

🙋🏼‍♀️🙋🏾‍♀️🙋🏽‍♀️ Let us know if you need any help! 🙋🏻‍♀️🙋🏽‍♂️🙋🏿‍♀️

Demo: Icon writer API


                def main(req: func.HttpRequest) -> func.HttpResponse:
                    text = get_param(req, 'text')
                    size = get_param(req, 'size', 80)
                    bgcolor = get_param(req, 'bgcolor', 'black')
                    fontcolor = get_param(req, 'fontcolor', 'white')
                    if text:
                        img = write_icon(text, size=size, bgcolor=bgcolor, fontcolor=fontcolor)
                        img_byte_arr = io.BytesIO()
                        img.save(img_byte_arr, format='PNG')
                        img_byte_arr = img_byte_arr.getvalue()
                        return func.HttpResponse(img_byte_arr, mimetype='image/png')
                    else:
                        return func.HttpResponse(
                            "Text must be specified",
                            status_code=400
                    )
                

👀 Demo: pamelafox.github.io/icon-writer-website/

👩🏼‍💻 Code: github.com/pamelafox/icon-writer-function/blob/main/IconWriter/__init__.py

More on APIs

Raccoons with laptops

POST APIs with FastAPI

If your API needs to support the creation of data, then requests should go over HTTP POST instead.

How POST requests are specified in FastAPI:


                from typing import Union

                from fastapi import FastAPI
                from pydantic import BaseModel

                class Item(BaseModel):
                    name: str
                    description: Union[str, None] = None
                    price: float
                    tax: Union[float, None] = None

                app = FastAPI()

                @app.post("/items/")
                async def create_item(item: Item):
                    return item
                

Learn more in FastAPI docs

APIs with Django REST

If your site uses a DB and the Django framework, the Django REST framework can be used to create CRUD APIs.


                from django.urls import path, include
                from django.contrib.auth.models import User
                from rest_framework import routers, serializers, viewsets

                class UserSerializer(serializers.HyperlinkedModelSerializer):
                    class Meta:
                        model = User
                        fields = ['url', 'username', 'email', 'is_staff']

                class UserViewSet(viewsets.ModelViewSet):
                    queryset = User.objects.all()
                    serializer_class = UserSerializer

                router = routers.DefaultRouter()
                router.register(r'users', UserViewSet)

                urlpatterns = [
                    path('', include(router.urls)),
                    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
                ]
                

Any questions?

A bunch of raccoon students with computers