HTTP Routing Query Body¶
Overview¶
This recipe covers a multi-endpoint HTTP API in examples/http/http_routing_query_body/
that demonstrates route parameters,
query strings,
JSON request parsing,
and conventional REST status codes.
The sample keeps state in an in-memory USERS dictionary,
which makes it ideal for learning endpoint behavior and request validation
without adding database complexity.
It includes list,
lookup,
create,
update,
delete,
and search flows in one function_app.py.
When to Use¶
- You need a compact reference for HTTP CRUD patterns in Python Azure Functions.
- You want examples of combining route params, query params, and JSON body parsing.
- You need status-code handling (
200,201,204,400,404,409) in one sample.
Architecture¶
+---------+ GET/POST/PUT/DELETE requests +-------------------------------+
| Client | ---------------------------------> | FunctionApp HTTP Routes |
+----+----+ | /users, /users/{user_id} |
| | /search?q=&limit= |
| +---------------+---------------+
| |
| v
| +-------------------------------+
| | Handlers + Helpers |
| | _parse_json_body(req) |
| | _json_response(body, status) |
| +---------------+---------------+
| |
| v
| +-------------------------------+
+----------------------------------------- | In-memory USERS dict |
| create/update/delete/search |
+-------------------------------+
Prerequisites¶
- Python 3.10+
- Azure Functions Core Tools v4
azure-functionspackage fromrequirements.txt- HTTP client tool such as
curlor Postman - Understanding that in-memory state resets when the host restarts
Project Structure¶
examples/http/http_routing_query_body/
├── function_app.py
├── host.json
├── local.settings.json.example
├── requirements.txt
└── README.md
Implementation¶
The example defines reusable helpers first,
then composes six route handlers around an in-memory USERS store.
Shared state and JSON helper:
USERS: dict[str, dict[str, str]] = {
"1": {"id": "1", "name": "Ada Lovelace", "email": "ada@example.com"},
"2": {"id": "2", "name": "Grace Hopper", "email": "grace@example.com"},
}
def _json_response(body: object, status_code: int = 200) -> func.HttpResponse:
return func.HttpResponse(
json.dumps(body),
status_code=status_code,
mimetype="application/json",
)
Robust JSON parsing:
def _parse_json_body(req: func.HttpRequest) -> dict[str, Any] | None:
try:
payload = req.get_json()
except ValueError:
return None
return payload if isinstance(payload, dict) else None
List and single-resource retrieval use route and store lookups:
@app.route(route="users", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def list_users(req: func.HttpRequest) -> func.HttpResponse:
del req
return _json_response({"users": list(USERS.values())})
@app.route(route="users/{user_id}", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
def get_user(req: func.HttpRequest) -> func.HttpResponse:
user_id = req.route_params.get("user_id", "")
user = USERS.get(user_id)
if user is None:
return _json_response({"error": f"User '{user_id}' not found."}, status_code=404)
return _json_response(user)
Create endpoint validates fields and conflict cases:
@app.route(route="users", methods=["POST"], auth_level=func.AuthLevel.ANONYMOUS)
def create_user(req: func.HttpRequest) -> func.HttpResponse:
payload = _parse_json_body(req)
if payload is None:
return _json_response({"error": "Request body must be a JSON object."}, status_code=400)
name = str(payload.get("name", "")).strip()
email = str(payload.get("email", "")).strip()
if not name or not email:
return _json_response({"error": "Fields 'name' and 'email' are required."}, status_code=400)
Update, delete, and search extend the same pattern:
update_userfetches byuser_id, validates body shape, and returns200.delete_userremoves existing entries and returns204with empty body.search_usersreadsqandlimit, validates integer conversion, and slices matches.
Example paths exposed by this app:
GET /api/users
GET /api/users/{user_id}
POST /api/users
PUT /api/users/{user_id}
DELETE /api/users/{user_id}
GET /api/search?q=ada&limit=5
Run Locally¶
Expected Output¶
GET /api/users
-> 200 OK
{"users":[{"id":"1","name":"Ada Lovelace","email":"ada@example.com"},...]}
POST /api/users with {"name":"Lin","email":"lin@example.com"}
-> 201 Created
{"id":"3","name":"Lin","email":"lin@example.com"}
DELETE /api/users/3
-> 204 No Content
Production Considerations¶
- Scaling: Replace in-memory
USERSwith durable storage because scale-out instances do not share process memory. - Retries: HTTP callers should retry transient
5xxresponses; avoid blind retries on409/400validation failures. - Idempotency: Design
PUTandDELETEsemantics for safe replay and use deterministic IDs when clients can retry. - Observability: Log route name, status code, and validation failures as structured fields for API diagnostics.
- Security: Upgrade auth level and enforce authn/authz before exposing user CRUD externally.