Middle+
How to implement the Waiter pattern for an API?
sobes.tech AI
Answer from AI
The Waiter pattern for API is implemented by periodically polling a resource until a specific target state is reached or a timeout occurs.
Main components of the implementation:
- Waiting goal: Define the condition under which the wait is considered complete (e.g., resource status becomes "ready", a field value reaches a certain value).
- Polling interval: The time between consecutive API requests.
- Timeout: The maximum time during which the Waiter will perform polls.
- Polling logic: A function or method that performs a GET request to the API to get the current state of the resource.
- State check: Logic that analyzes the API response and checks if the target state has been reached.
- Waiting mechanism: An implementation of a loop that performs polls at a set interval, checks the state, and terminates upon reaching the goal, timeout, or error.
Example implementation in Python:
import time
import requests
from typing import Dict, Any, Optional, Callable
def wait_until(
url: str,
api_key: str,
condition: Callable[[Dict[str, Any]], bool],
timeout: int = 60,
polling_interval: int = 5
) -> Dict[str, Any]:
"""
Waits until the resource at the given URL satisfies the condition.
Args:
url: API resource URL.
api_key: API key for authentication.
condition: Function that takes the response (dict) and returns True if the condition is met.
timeout: Maximum wait time in seconds.
polling_interval: Interval between requests in seconds.
Returns:
The last response received from the API when the condition was met.
Raises:
TimeoutError: If the condition is not met within the specified timeout.
requests.exceptions.RequestException: If an HTTP request error occurs.
"""
start_time = time.time()
headers = {"X-API-Key": api_key} # Example header for API key
while time.time() - start_time < timeout:
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raises exception for bad statuses (4xx or 5xx)
data = response.json()
if condition(data):
return data # Condition met
except requests.exceptions.RequestException as e:
print(f"Request error: {e}")
# Optional: add retry logic or exit
time.sleep(polling_interval) # Wait before next poll
raise TimeoutError(f"Waiting for condition on {url} timed out ({timeout} seconds)")
# Usage example:
# Assume the API returns a processing status in the 'status' field
# and we wait until the status becomes 'COMPLETED'
# resource_url = "https://api.example.com/processing_job/123"
# my_api_key = "YOUR_API_KEY"
# def is_completed(data: Dict[str, Any]) -> bool:
# return data.get("status") == "COMPLETED"
# try:
# completed_resource_data = wait_until(
# resource_url,
# my_api_key,
# is_completed,
# timeout=120,
# polling_interval=10
# )
# print("Resource is ready:", completed_resource_data)
# except (TimeoutError, requests.exceptions.RequestException) as e:
# print("Failed to wait for resource readiness:", e)
Implementation features:
- Error handling: Handle HTTP errors (bad statuses, network issues).
- Exponential backoff: Increase the polling interval after each failed attempt to reduce load on the API.
- Cancellation: Provide a way to interrupt the wait.
- Logging: Add logging for progress tracking and issues.
- Parameterization: Make the Waiter flexible, allowing configuration of URL, request method (GET, POST), headers, parameters, and wait conditions.