Back to Blog
Part 4 of 8
AzureServerlessAzure FunctionPythonFlask

Azure Functions Tutorial: Writing an API

October 1, 20222 min read
Share:

Writing an API with Flask#

Why Flask?

There's no set rule for developing APIs on Azure Functions. You can have one-function-one-api or integrate frameworks like Flask or FastAPI. We'll use Flask to enable easy migration of existing Flask apps.

Add Dependencies#

Update requirements.txt:

azure-functions
flask
flask-cors
flask-restful
pyjwt
werkzeug
azure-identity
azure-keyvault-secrets
opencensus-ext-azure
opencensus-ext-logging
opencensus-ext-flask
opencensus-ext-requests
opencensus-extension-azure-functions

Create Flask App#

Create the folder structure:

.
├── functiondemo/
│   ├── __init__.py
│   └── function.json
└── src/
    └── api/
        └── __init__.py

Flask App Code (src/api/__init__.py)#

import time
from flask import Flask, jsonify, request, Response, g
from flask_cors import CORS, cross_origin
import logging

app = Flask(__name__)
cors = CORS(origins='*', allow_headers=['Content-Type', 'Authorization'])
logger = logging.getLogger(__name__)

@app.before_request
def before_request():
    g.start = time.time()

@app.after_request
def response_logger(response: Response):
    response_time = round(time.time() - g.start, 3) * 1000
    ip = request.headers.getlist("X-Forwarded-For")[0] if request.headers.getlist("X-Forwarded-For") else request.remote_addr
    logger.info(f"addr={ip} method={request.method} url={request.url} response_time_ms={response_time}")
    return response

@app.errorhandler(404)
def handle_not_found(ex):
    return jsonify(message=f"Path {request.path} not found"), 404

@app.route("/health", methods=['GET', 'HEAD'])
def health():
    return jsonify(message="Health Check Ok"), 200

@app.route("/vault", methods=['GET', 'HEAD'])
@cross_origin(cors)
def get_secret():
    secret = request.args.get("secret")
    return jsonify(message=f"Vault Integration not implemented for {secret}"), 200

Connect to Azure Function#

Update functiondemo/__init__.py:

import azure.functions as func
from src.api import app as application

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return func.WsgiMiddleware(application.wsgi_app).handle(req, context)

Configure Routing#

Update functiondemo/function.json:

{
  "scriptFile": "__init__.py",
  "bindings": [{
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": ["get", "post", "head"],
    "route": "{*route}"
  }, {
    "type": "http",
    "direction": "out",
    "name": "$return"
  }]
}

Remove the /api prefix in host.json:

{
  "extensions": {
    "http": {
      "routePrefix": ""
    }
  }
}

Test & Deploy#

# Test locally
http://localhost:7071/health
http://localhost:7071/vault?secret=demo

# Deploy
git push origin master
YS

Yogesh Sharma

Principal Software Engineer @ Intuit