Building Asynchronous APIs
Asynchronous design is valuable for APIs that don’t require instant feedback or have processes that could be delayed due to complexity, resource consumption, or dependency on external systems. Building it is an intricate topic. This requires balancing the requirements for responsiveness and the server’s capability to handle long-running operations without compromising performance.
Let’s dive into designing asynchronous REST JSON APIs and SOAP APIs from a technical architecture perspective, with detailed flow examples and a few illustrative Mermaid sequence diagrams.
Why Asynchronous APIs?
In synchronous APIs design, each client request waits for a response before proceeding, making them intuitive but potentially slow for resource-heavy or long-running tasks. On the other hand, Asynchronous APIs allow clients to submit a request and proceed without waiting, which is valuable for:
- Resource-intensive operations: Such as processing large data files, running complex calculations, or waiting on third-party responses.
- User experience enhancement: Allowing the client (especially in UI applications) to move on to other tasks while the operation completes.
- Scalability and resilience: Handling large volumes of requests without bogging down system resources.
For example, a photo sharing platforms often adopt asynchronous design to handle tasks for uploaded photos/images like sampling, rendering, replication, etc. Let’s explore this through practical architectural patterns.
- Request: The client uploads an image file.
- Acknowledgment: The server acknowledges receipt with an immediate response, providing an ID for polling. This is a synchronous behavior.
- Processing: The platform converts/processes this image in the background. For example, down-sampling for faster rendering.
- Notification: The server either triggers a webhook or awaits the client’s periodic polling. When the client is polling, the ID is used.
This setup balances client and server demands. By optimizing polling intervals and providing flexible notification options, the service can handle high traffic without overwhelming system resources.
Design Consideration For Asynchronous API
The design of asynchronous APIs often revolves around three main components:
- Client Request: The client initiates the process, typically through a REST or SOAP call.
- Task Processor: An internal or external service that handles the actual job processing.
- Polling or Webhooks: Mechanisms to update the client about the task’s progress or completion.
Polling Approach
In the polling method, the client submits an image processing request and periodically checks the server for the status of the task.
Workflow:
- Request Submission: The client sends a
POST
request to the server to initiate image processing.
POST /api/v1/process-image
Content-Type: application/json
{
"image_url": "http://example.com/image.jpg",
"transformations": ["resize", "filter"]
}
- Acknowledgment: The server responds with a
202
Accepted status, providing a unique job ID and a status endpoint.
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"job_id": "12345",
"status_url": "/api/v1/process-image/status/12345"
}
- Status Polling: The client periodically sends
GET
requests to the status URL to check the progress.
GET /api/v1/process-image/status/12345
- Completion Notification: Once processing is complete, the server responds with a
200 OK
status and the URL of the processed image.
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "completed",
"processed_image_url": "http://example.com/processed_image.jpg"
}
Mermaid Sequence Diagram:
This approach is straightforward but can lead to increased server load and network traffic, especially if many clients poll frequently. Adaptive polling strategies, such as exponential backoff, can mitigate some inefficiencies by adjusting the polling frequency based on the expected processing time.
Callback Webhook Approach
The callback webhook method allows the server to notify the client upon task completion, eliminating the need for polling Here is a data workflow for using callbacks.
- Request Submission: The client sends a
POST
request to the server to start processing an image, including a callback URL.
POST /api/v1/process-image
Content-Type: application/json
{
"image_url": "http://example.com/image.jpg",
"transformations": ["resize", "filter"],
"callback_url": "http://client.com/webhook"
}
- Acknowledgment: The server responds with a
202
Accepted status and a unique job ID.
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"job_id": "12345"
}
- Processing: The server processes the image asynchronously.
Callback Notification: Upon completion, the server sends a
POST
request to the client's callback URL with the processed image details.
POST /webhook
Content-Type: application/json
{
"job_id": "12345",
"status": "completed",
"processed_image_url": "http://example.com/processed_image.jpg"
}
Sequence Diagram:
Compare: Polling Vs Callback
The choice between polling and webhooks depends on factors such as client capabilities, network reliability, and security considerations.
Aspect | Polling | Callback Webhooks |
---|---|---|
Advantages | - Simple to implement; the client controls the timing of status checks. - No need for the client to expose an endpoint for callbacks. | - Efficient resource usage; no need for repeated polling. - Immediate notification upon task completion. |
Disadvantages | - Inefficient resource usage due to repeated polling requests. - Potential delays in client awareness of task completion. | - The client must expose an endpoint to receive callbacks, which may have security implications. - Additional complexity in handling incoming webhook requests. |
Use When | - The client cannot expose a public endpoint. - The expected processing time is short, reducing the impact of polling. | - Immediate notification is critical. - The client can securely handle incoming requests. |
Design Considerations
Several factors need careful planning and designing for the API interaction and flow when designing an effective asynchronous API. Here are some areas to consider.
- Timeouts and Retry Logic: Since processing can be delayed, set sensible timeout limits. For example, AWS Lambda functions have a maximum timeout of 15 minutes.
- Task Prioritization: Some tasks may need to be prioritized based on urgency or processing cost, such as high-priority messages in messaging APIs.
- Client Expectations: Clear API documentation helps clients understand response times and result retrieval methods. This is critical for user experience.
- Real-World Example: Asynchronous Document Processing API
Build & Test With Beeceptor
Beeceptor's HTTP Proxy Callout feature enables you to simulate asynchronous APIs by forwarding incoming HTTP requests to specified endpoints, either synchronously or asynchronously. In asynchronous mode, Beeceptor immediately returns a predefined response to the client while concurrently initiating a background HTTP callout to the target service.
Conclusion: Polling v/s Webhooks
Polling is easy to implement solution. The client can pick polling frequency based on the urgency or historical data. However this observer delays. Whereas, webhooks provide a more scalable approach but require additional setup and security considerations on the client side.
Asynchronous API design requires a deep understanding of operation, data exchange flow, domain, client requirements, and system capacity. With careful orchestration, asynchronous APIs can significantly enhance scalability and user experience.