Trigger an Azure Function with HTTP

Learn to trigger an Azure Function via HTTP. This Azure Tutorial lesson shows how to set up an HTTP trigger, test it, and troubleshoot common issues. Build your Azure skills step by step.

Focus: trigger a function with http

Sponsored

You’ve deployed Azure resources, wired up managed identities, and stored secrets. But how do you actually do something when the outside world calls in? Every API, webhook, or chatbot starts with a single entry point — and in Azure Functions, that entry point is the HTTP trigger. Without it, your serverless logic sits idle, unreachable. This lesson shows you how to trigger a function with HTTP, turning a simple endpoint into a live, testable service that responds in milliseconds — and how to do it without tripping over the infamous gotchas that waste hours.

The Problem This Lesson Solves

Imagine you’ve built a Python function that processes order data or validates a token. It works when you run it locally, but how do you let a web app, a mobile client, or a third-party service call it? The gap between “code that runs” and “code that responds to a request” is exactly where HTTP triggers shine.

The pain points are real:

  • No URL — your function has no address on the internet, so nothing can reach it.
  • No payload handling — you don’t know how to read query strings, headers, or JSON bodies correctly.
  • No status codes — you return 200 OK for everything, masking errors.
  • No security checks — you leave the endpoint open, inviting abuse.

This lesson fixes all four. By the end, you’ll have a function that listens on a public HTTPS endpoint, parses input, returns proper responses, and rejects unauthorized calls — ready for production.

Core Concept / Mental Model

Think of an HTTP trigger as a receptionist at a hotel. The receptionist (your function) sits at a desk (the endpoint). When a guest (an HTTP request) arrives, the receptionist:

  1. Checks the guest’s ID — is there a function key or authorization code?
  2. Reads the request — looks at the method (GET/POST), the query string, headers, and body.
  3. Performs the task — calls your Python logic.
  4. Prints a receipt — returns an HTTP response with a status code and payload.

This is exactly how a function with an HTTP trigger works. The key components:

  • Trigger binding — the @app.route decorator in Python (or HttpTrigger in other languages) that tells Azure Functions “listen on this route.”
  • Request object — carries method, headers, query parameters, and JSON body.
  • Response object — carries status code, body, and headers back to the caller.

In the Azure Functions Python model (v2+), you define the route inside the function_app.py file. Here’s the anatomy (in words):

function_app.py
   └── @app.route(route="hello", methods=["GET"])
        └── def hello(req: func.HttpRequest) -> func.HttpResponse:
             ├── read query/body
             ├── run your logic
             └── return response

Now let’s see it in action.

How It Works Step by Step

Triggering a function with HTTP follows a predictable flow. Here’s the logical sequence, cause → effect:

  1. Define the route — In function_app.py, you decorate a function with @app.route. The route parameter is the URL path (e.g., /api/hello).
  2. Set allowed methods — Specify methods=["GET", "POST"] or any combination. The function only responds to those methods; others return 405 Method Not Allowed.
  3. Receive the request — Azure Functions injects a func.HttpRequest object into your function. This object gives you access to: - req.method (string like 'GET') - req.params (dictionary of query parameters) - req.headers (request headers) - req.get_json() (parsed JSON body, if any)
  4. Read input — Use req.params.get('name') for query params, or req.get_json() to parse a JSON payload. Always wrap get_json() in a try/except — a malformed body raises an error.
  5. Process and respond — Run your business logic, then return a func.HttpResponse with a status code (200, 400, 500) and a body (JSON or text).
  6. Handle auth (optional) — By default, functions require an authorization level. Set auth_level=func.AuthLevel.ANONYMOUS for open access, or FUNCTION (default) to require a function key in the x-functions-key header.

The order matters: parse input first, then validate, then process, then respond. Never skip validation.

Hands-On Walkthrough

Let’s build a real HTTP-triggered function. We’ll create a function that accepts a name in a query string or JSON body and returns a personalized greeting.

Prerequisites

  • Azure Functions Core Tools installed (func --version)
  • Azure CLI installed and logged in
  • Python 3.10+ installed

Step 1: Create a new Function App

# Create a folder for the project
mkdir http-trigger-demo && cd http-trigger-demo

# Initialize a Python function app
func init --python

# Add a new function with an HTTP trigger
func new --name http_hello --template "HTTP trigger"

This creates a function_app.py file with a basic function. Let’s replace it with a more robust version.

Step 2: Write the function code

Replace the contents of function_app.py with the code below. It reads a name parameter from the query string or JSON body, validates it, and returns a greeting.

import azure.functions as func
import json
import logging

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.route(route="hello", methods=["GET", "POST"])
def hello(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    # Try to get name from query parameter first
    name = req.params.get('name')

    # If not in query, try the JSON body
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            req_body = None
        if req_body and 'name' in req_body:
            name = req_body['name']

    # Validate input
    if name:
        return func.HttpResponse(
            json.dumps({"message": f"Hello, {name}! This HTTP triggered function executed successfully."}),
            status_code=200,
            mimetype="application/json"
        )
    else:
        return func.HttpResponse(
            json.dumps({"error": "Please pass a name on the query string or in the request body"}),
            status_code=400,
            mimetype="application/json"
        )

Step 3: Run locally and test

# Start the function app locally
func start

You’ll see output like:

Functions:
    http_hello: [GET,POST] http://localhost:7071/api/hello

Now test it with curl:

# Test with query string
curl -w "\nHTTP Status: %{http_code}\n" "http://localhost:7071/api/hello?name=AzureFan"

Expected output:

{"message": "Hello, AzureFan! This HTTP triggered function executed successfully."}
HTTP Status: 200

Test with a JSON body:

curl -X POST -H "Content-Type: application/json" \
     -d '{"name": "DevOpsPro"}' \
     -w "\nHTTP Status: %{http_code}\n" \
     http://localhost:7071/api/hello

Expected output:

{"message": "Hello, DevOpsPro! This HTTP triggered function executed successfully."}
HTTP Status: 200

Test the error path:

curl -w "\nHTTP Status: %{http_code}\n" http://localhost:7071/api/hello

Expected output:

{"error": "Please pass a name on the query string or in the request body"}
HTTP Status: 400

Pro tip: Use -w in curl to print the HTTP status code. It’s the fastest way to check that your function is returning the right status.

Step 4: Deploy to Azure

When you’re ready to go live:

# Log in to Azure
az login

# Create a resource group and function app (replace names as needed)
az group create --name rg-http-demo --location eastus
az functionapp create --resource-group rg-http-demo --consumption-plan-location eastus \
  --runtime python --runtime-version 3.11 --functions-version 4 \
  --name my-http-function-app --storage-account mystorageaccount123

# Deploy your code
func azure functionapp publish my-http-function-app

After deploy, you’ll get an endpoint like: https://my-http-function-app.azurewebsites.net/api/hello?name=Cloud

Test it:

curl "https://my-http-function-app.azurewebsites.net/api/hello?name=Cloud"

Compare Options / When to Choose What

Not all HTTP triggers are the same. You can customize them to suit your needs. Here’s how to choose:

Configuration Use case Example
Route parameters RESTful APIs where you want the ID in the URL path /api/users/{id}
Query parameters Simple filters or optional input ?status=active
JSON body POST/PUT with complex payloads POST /api/orders with JSON
Headers Authentication tokens or custom metadata Authorization: Bearer ...
Auth level: ANONYMOUS Public endpoints (e.g., product catalog) No key required
Auth level: FUNCTION (default) Private APIs, webhooks that require a key Must pass x-functions-key
Auth level: ADMIN Highest security — master key only Rarely used; for admin operations

When to use route parameters vs query parameters:

  • Use route parameters when the value is part of the resource identity (e.g., GET /api/users/42).
  • Use query parameters for optional filters or actions (e.g., GET /api/users?role=admin).

When to require a function key: - If your function will be called by server-to-server with a shared secret, use FUNCTION level. Always pass the key in the x-functions-key header. - If it’s a public endpoint (like a status page), set ANONYMOUS but be aware of abuse — add rate limiting or a gateway.

Troubleshooting & Edge Cases

1. Function returns 401 Unauthorized

Cause: Your function requires a function key but you didn’t provide one. Fix: Set auth_level=func.AuthLevel.ANONYMOUS if it’s a public endpoint, or include the key in your request:

curl -H "x-functions-key: YOUR_FUNCTION_KEY" https://your-app.azurewebsites.net/api/hello

To get the key: in the Azure portal, go to your function → App keysHost keys → copy the default key.

2. get_json() raises ValueError on malformed body

Symptom: If you send POST with an empty body or invalid JSON, the function throws a 500 error. Fix: Always wrap get_json() in a try/except, as shown in the code above. Return a 400 error with a friendly message.

3. Function returns 404 Not Found for a valid route

Cause: The route name doesn’t match. Azure Functions appends /api/ by default. If your route is hello, the URL is /api/hello. If you pass a leading slash or different case, you get 404. Fix: Use the exact route as shown in the function’s Code + Test blade or func start output.

4. Query parameter is always None

Issue: You send ?name=test but req.params.get('name') returns None. Reason: Possibly a typo in the parameter name, or the request is a POST and the parameter is in the body. Double-check the spelling and the method.

5. Response is not JSON even when you set mimetype

Symptom: You set mimetype="application/json" but clients see text/plain. Fix: Ensure you also serialize the body with json.dumps(). Azure Functions sets the Content-Type from the mimetype parameter, but if the body is not a JSON string, clients may parse it as plain text.

What You Learned & What's Next

You’ve just learned how to trigger a function with HTTP. Let’s recap the core takeaways:

  • HTTP triggers give your function a public URL, turning code into an API endpoint.
  • You now know how to read query strings, JSON bodies, and headers using the func.HttpRequest object.
  • You can control the response with status codes, JSON payloads, and proper mimetype.
  • You can secure your endpoint with function keys or leave it anonymous, depending on the use case.

This is the backbone of serverless APIs. Next, you’ll extend this concept by connecting your function to other services — for example, writing the incoming data to Azure Blob Storage or sending a message to a queue. That’s where HTTP triggers become truly powerful: they become the entry point for event-driven workflows.

Up next, keep building your Azure mastery. You’ve got the entry point — now you’ll give it something to do.

Practice recap

Extend the hello function you built to accept a POST with a JSON {"name": "...", "age": 25}. Return a message that includes the age, and return 400 if either field is missing. Test it with curl both locally and after deployment. You’ll reinforce parsing, validation, and structured responses — the exact skills you’ll need for the next lesson on connecting your function to storage.

Common mistakes

  • Forgetting to set auth_level=func.AuthLevel.ANONYMOUS on the FunctionApp object, causing unexpected 401 errors when testing locally.
  • Skipping the try/except around req.get_json() — a malformed JSON body then crashes the function with 500.
  • Confusing route parameters with query parameters — using {id} in the route but then trying to read it from req.params instead of req.route_params.
  • Assuming the function URL is just /hello — Azure Functions always prefixes /api/ unless you disable it, leading to 404s.
  • Returning func.HttpResponse(body) without setting mimetype="application/json" — clients receive text/plain and fail to parse.

Variations

  1. Use Flask or FastAPI locally to prototype the same logic, then port to Azure Functions — good for fast iteration.
  2. Add a route parameter like @app.route(route="users/{id}") and read it with req.route_params.get('id') for RESTful patterns.
  3. Deploy the function with a custom domain and API Management (APIM) in front for advanced routing, rate limiting, and versioning.

Real-world use cases

  • Building a public REST API for a mobile app — e.g., a function that returns product inventory from a database.
  • Creating a webhook receiver that accepts POST requests from external services (like Stripe) and processes payment events.
  • Exposing a serverless endpoint for a CI/CD pipeline to trigger an Azure Function that validates and deploys infrastructure changes.

Key takeaways

  • An HTTP trigger turns a Python function into a callable endpoint with a URL, methods, and status codes.
  • Use req.params for query strings, req.get_json() for bodies, and always wrap JSON parsing in try/except.
  • Control responses with func.HttpResponse, setting the status_code and mimetype for API consistency.
  • Set auth_level to ANONYMOUS for public endpoints or FUNCTION to require a function key.
  • Route parameters ({id}) are ideal for RESTful resource paths; query parameters suit filters and optional inputs.
  • Local debugging with func start is fast — use curl -w to check HTTP status codes and validate behavior before deploying.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.