Classroom "rules":
Tell us about yourself:
Codespaces is an online development environment.
Open a GitHub repo in Codespaces by clicking Code button, selecting Codespaces tab, and clicking Create codespace on main.
Then wait patiently... βΊοΈ
60 hours of free usage each month. π Tips for optimizing quotas
Option 1: Online development with Codespaces
Option 2: Local development with VS Code
Option 3: Local development
A client sends an HTTP request:
GET /index.html HTTP/1.1
Host: www.example.com
A 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>
The most commonly used codes:
Code | Meaning |
---|---|
200 | OK |
301 | Moved Permanently |
404 | Not Found |
500 | Server Error |
See more codes at HTTPcats.com or Wikipedia: HTTP status codes.
A way for one program to talk to another program.
Example:
from weather import forecast
todays_forecast = forecast(94702)
The weather
module's API includes a forecast
function
that takes a zip code and returns a forecast.
Any API that uses HTTP as its communication protocol.
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}
{"temperature": 70, "wind": 5}
<weather-response>
<temperature>70</temperature>
<wind>5</wind>
</weather-response>
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
Use urllib, requests, or urllib3 to make HTTP requests.
import urllib3
resp = urllib3.request("GET",
"https://api.zippopotam.us/us/94530")
result = resp.json()
Try the APIs below, customizing the URLs as suggested:
You can either try them in the browser or try parsing them in Python and printing the results.
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.
An API can also be setup solely for use by the company that made it.
Many websites have a separate codebase for their frontend and backend, and all the communication happens over internal APIs.
Internal APIs should still be documented and easy to use.
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
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
FastAPI is a Python framework designed specifically for building HTTP APIs.
import random
import fastapi
app = fastapi.FastAPI()
@app.get("/generate_name")
async def generate_name():
names = ["Minnie", "Margaret", "Myrtle", "Noa", "Nadia"]
random_name = random.choice(names)
return {"name": random_name}
π©πΌβπ» Want to follow along? Starter repo:
https://github.com/pamelafox/pyday-fastapi-starter
1. Put code in api/main.py
2. Install requirements
pip install fastapi
pip install "uvicorn[standard]"
3. Run the server
uvicorn api.main:app --reload --port=8000
4. Try the API and docs
http://127.0.0.1:8000/generate_name
http://127.0.0.1:8000/docs
import random
import fastapi
app = fastapi.FastAPI()
@app.get("/generate_name")
async def generate_name(max_len: int = None):
names = ["Minnie", "Margaret", "Myrtle", "Noa", "Nadia"]
if max_len:
names = [n for n in names if len(n) <= max_len]
random_name = random.choice(names)
return {"name": random_name}
FastAPI also supports passing parameters in the path, cookies, headers, or body.
import random
import fastapi
app = fastapi.FastAPI()
@app.get("/generate_name")
async def generate_name(max_len:int = None):
names = ["Minnie", "Margaret", "Myrtle", "Noa", "Nadia"]
if max_len:
names = [n for n in names if len(n) <= max_len]
if len(names) == 0:
raise fastapi.HTTPException(status_code=404, detail="No name found")
random_name = random.choice(names)
return {"name": random_name}
Starting from this repo:
github.com/pamelafox/simple-fastapi-azure-function
ends_with
,
includes
, or length
.
ππΌββοΈππΎββοΈππ½ββοΈ Let us know if you need any help! ππ»ββοΈππ½ββοΈππΏββοΈ
Gunicorn is a production-level server that can run multiple worker processes.
Add gunicorn
to requirements.txt
:
fastapi
uvicorn[standard]
gunicorn
Use gunicorn
to run FastAPI app using Uvicorn
worker:
python3 -m gunicorn api.main:app --workers 4 \
--worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Gunicorn can be configured with a gunicorn.conf.py
file
to adjust worker count based on CPU cores.
import multiprocessing
max_requests = 1000
max_requests_jitter = 50
log_file = "-"
bind = "0.0.0.0:8000"
worker_class = "uvicorn.workers.UvicornWorker"
workers = (multiprocessing.cpu_count() * 2) + 1
The run command can be simplified to:
python3 -m gunicorn main:app
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.
Using the Azure Dev CLI:
An Azure API Management Policy provides all the additional features of a public API.
Azure CDN provides a global network of servers to cache your API responses.
π Demo: staticmaps-rk5lctcdqzvbs-cdn-endpoint.azureedge.net
π©πΌβπ» Code: github.com/pamelafox/staticmaps-function
Starting from this repo (or your fork):
github.com/pamelafox/simple-fastapi-azure-function
azd up
. If prompted, login to your Azure account.
azd down
to un-deploy the app (so that you don't waste cloud resources unnecessarily).
ππΌββοΈππΎββοΈππ½ββοΈ Let us know if you need any help! ππ»ββοΈππ½ββοΈππΏββοΈ
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
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
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
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'))
]