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
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:
- Checks the guest’s ID — is there a function key or authorization code?
- Reads the request — looks at the method (GET/POST), the query string, headers, and body.
- Performs the task — calls your Python logic.
- 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.routedecorator in Python (orHttpTriggerin 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:
- Define the route — In
function_app.py, you decorate a function with@app.route. Therouteparameter is the URL path (e.g.,/api/hello). - Set allowed methods — Specify
methods=["GET", "POST"]or any combination. The function only responds to those methods; others return 405 Method Not Allowed. - Receive the request — Azure Functions injects a
func.HttpRequestobject 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) - Read input — Use
req.params.get('name')for query params, orreq.get_json()to parse a JSON payload. Always wrapget_json()in a try/except — a malformed body raises an error. - Process and respond — Run your business logic, then return a
func.HttpResponsewith a status code (200, 400, 500) and a body (JSON or text). - Handle auth (optional) — By default, functions require an authorization level. Set
auth_level=func.AuthLevel.ANONYMOUSfor open access, orFUNCTION(default) to require a function key in thex-functions-keyheader.
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
-wincurlto 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 keys → Host 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.HttpRequestobject. - 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.ANONYMOUSon theFunctionAppobject, 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 fromreq.paramsinstead ofreq.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 settingmimetype="application/json"— clients receive text/plain and fail to parse.
Variations
- Use Flask or FastAPI locally to prototype the same logic, then port to Azure Functions — good for fast iteration.
- Add a route parameter like
@app.route(route="users/{id}")and read it withreq.route_params.get('id')for RESTful patterns. - 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.paramsfor query strings,req.get_json()for bodies, and always wrap JSON parsing in try/except. - Control responses with
func.HttpResponse, setting thestatus_codeandmimetypefor API consistency. - Set
auth_leveltoANONYMOUSfor public endpoints orFUNCTIONto require a function key. - Route parameters (
{id}) are ideal for RESTful resource paths; query parameters suit filters and optional inputs. - Local debugging with
func startis fast — usecurl -wto check HTTP status codes and validate behavior before deploying.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.