When building an API, the stakes are often high. It is crucial to create a great API that developers want to use. While building an API, engineers typically think about:
- What should be the HTTP method -
DELETE, or something else?
- What should be the endpoint/path?
- What should be the request parameters?
- What should be the response structure?
Another factor to consider is whether this will be a synchronous (sync) or asynchronous (async) API call.
Synchronous API calls are blocking calls that do not return until the operation is complete or errored.
- User login
- Data Lookups
- Typical CRUD (Create, Read, Update, Delete) operations
- Blocks the caller till the processing is complete. This is the most natural way people think about the request-response model.
- Useful when a caller immediately needs to know if the operation succeeded or failed.
- Testing is as easy as sending requests using tools like Postman/curl and asserting on the response. Most of the automation frameworks support testing for these types of requests out of the box.
- Sync APIs are typically wrapped in a database transaction, which helps in either committing or rolling back the whole operation.
- In case of issues, debugging across the flow is straightforward, even if it involves many microservices.
- If used incorrectly, long-running sync operations can give the illusion of a system hang, which leads to user frustration. Sync APIs should be preferred when operations can be completed in a reasonable amount of time a user can wait.
- If there are many downstream calls, the call can take an enormous amount of time and the client can close the connection after the timeout. Even if each call is fast individually, the total request time can often exceed standard timeouts, leading to inconsistent behavior.
- Developers need to make sure each sync call must have a reasonable timeout. The default timeout of most web frameworks is 1 minute, which is more than a typical user is ready to wait for an operation to complete.
- The system should have a provision to rate limit or throttle a misbehaving caller to prevent it from taking the system down.
Asynchronous APIs return response immediately with the information to check the status of the operation later.
- Generating a report which takes a few minutes to run
- Sending an email
- APIs to receive callbacks
- The caller system gets the control back immediately, so it can continue its processing. Such systems feel snappy/fast to the user.
- Typically preferred when the operation can take a long/non-deterministic time to complete.
- Async APIs decouple systems from each other's dependencies, errors, and availability.
- Eliminates sync dependency on other systems, allowing for more deliberate failure handling built into the approach.
- Since the result can be checked later, the system can generate intermediate results. These intermediate results can be communicated to the caller (push) or checked by the caller (pull/poll). In some cases, where there is a need to generate multiple responses to a single request, this is a great option.
- Even if a single request kicks off a complex downstream workflow, the caller doesn't need to wait for the entire workflow to complete.
While being good at unblocking callers for further processing, async APIs do open up new problems to tackle:
- Instead of the caller knowing the result of the operation immediately, it needs to develop a separate mechanism to check the result. In simple cases, the caller can poll at regular intervals which might waste CPU cycles and network bandwidth. In complex cases, you might have to invest in developing push notification / event-driven infrastructure between systems.
- Since it's not easy to wrap everything in a single database transaction, the system needs to create mechanisms to maintain the integrity of the data through either idempotency, retries, or (potentially complex) rollback approaches.
- In the case of distributed async systems, tracking the status of operation becomes difficult. So you need to invest in better tools for observability and telemetry.
- Sometimes there needs to be an approach for replays from a point in time.
- Most of the frameworks do not support writing unit/integration tests for async APIs out of the box.
Given the rise of complex web applications, APIs are becoming more and more complex. Asynchronous APIs are a tool that every developer needs to have in their toolkit. But, the choice between sync and async APIs needs to be explicit by weighing their pros and cons. Using the incorrect type of APIs will degrade the user experience of the system. Hopefully, this blog post gives you some insightful tips that will assist you in making that decision.