The performance of a Web API is critical for the end-user application to achieve its goals.
Short response times contribute to having more users perform key activities such as making online purchases. It’s also decisive for better user engagement, which translates into more user interactions.
On the other hand, poor performance will reduce user engagement and result in a lower completion rate of key activities. And that’s the best case scenario. 40% of users will actually abandon a website if it takes 3 seconds to load and for each extra second, it repels 10% of the remaining users. This also decreases the chances for new visitors to find the site since web search engines penalize bad performance with lower-ranked positions.
In this post, we’ll go over some basic ways to tune your Web API for better performance.
Scale with REST
REST API’s are the most popular type of Web API — and have been since about ~2008.
REST’s widespread adoption is rooted in its simplicity and in the way it takes advantage of HTTP, the protocol that the World Wide Web is based on.
Typically these API’s use JSON as the data exchange format which, compared to XML (the formerly most popular exchange format), is more compact. This, of course, makes it more efficient for transmission.
In addition, REST is stateless, which means that the server does not need to ‘remember’ each call in order to process subsequent calls. Each request contains all of the information required in order to be understood and processed. Statelessness helps in scaling the APIs to millions of concurrent users by deploying it to multiple servers. Any server can handle any request because there is no session-related dependency.
Beyond the benefits above mentioned, REST API’s are considered, in practice, a de facto standard. There is a huge ecosystem of tools, protocols and programming languages that support it, not to mention a global community of software developers continuously embracing it for new solutions.
Keep it nicely documented
A well-documented, clear and simple interface is a ‘must-have’ that helps application developers be more productive when it comes to exchanging data with the API.
Tools like Swagger UI or ReDoc provide interactive web-based documentation for your API based on the Open API Standard. Thanks to these, documenting an API is no longer a separate task. That’s efficiently taken care of when you include comments while writing the source code.
Consider using GraphQL
GraphQL is a query language for API’s. It allows clients to request data from the API by specifying the exact response structure that’s needed, even if it involves more than one resource at the same time.
GraphQL improves network usage efficiency by saving the client from having to make multiple round-trips to the server, and by requesting only the data that is strictly needed for the client app in a given moment. This may be a really good choice when the bandwidth is quite low, or when the connection quality is not optimal, such is often the case in mobile contexts.
GraphQL implementations offer ‘introspection’, a powerful feature that allows querying of the API schema, including available types (with their fields definitions) and queries.
Be on top of your database access
When it’s time to choose a type of database to use, several factors should be considered:
- Volume of data
- Common usage
- Speed of access
It’s quite common to observe performance bottlenecks caused by the way an API accesses the database. Let’s go over some of the most common actions we can take to improve data access performance:
- Fine-tune queries that do not perform optimally. Fetch only the data that’s strictly needed in order for the request to be processed.
- Return paged results instead of complete resultsets.
- If a specific query is sent to the database over and over again, consider caching results at least for the request lifetime.
- If you’re using an ORM (Object-relational mapping), make sure you are not running into the “N + 1 Queries” problem, and apply eager loading when necessary.
- For API requests that perform write operations to the database, avoid running a database query just to return data to the caller, especially if the resulting state is deterministic. Most of the times, just returning the newly created identifier (if any), is enough. Very often, read operations are more expensive than write operations.
Don’t have callers waiting
If some task the API performs involves sending an email notification, do not run this in the same thread in which the main task is being executed. A connectivity imperfection with the mail server could tack on seconds — time during which the user has abandoned the application.
In general, it’s important to identify tasks that an API does not have to run during the request lifetime, i.e., that can be executed in a separate thread or process.
Specific techniques for this depend on the design of your solution but it might involve any of the following:
- Asynchronous programming
- ‘Fire-and-forget’ calls
- Queue-based message brokers
- Triggering an external process
Keep in mind that a user is waiting for your response so deliver it as soon as possible.
WebSocket protocol enables two-way communication between a client application and the server. This allows pushing notifications to be sent to clients whenever something of interest has happened. In this way, clients don’t have to poll the server to ‘catch’ some event.
In addition, this protocol is fit for streaming, which consists of transmitting data as a continuous flow, allowing the client to start reading data as soon as there is some available. Even when data is still being received, the client can start reading data. A typical example of this is video streaming.
In some cases, the default JSON serializer/deserializer a programming language/framework offers is not the one with the best performance overall. Explore faster JSON serialization libraries that are available for your programming language of choice.
Make sure that the web server hosting your API supports HTTP compression and enable it. This is an easy and effective way to reduce the size of both request and response payloads, thus increasing the speed.
HTTP/2 transfers data in binary representations (instead of textual ones), making it a much faster protocol than HTTP/1.x. It uses multiplexed streams, allowing for multiple requests and responses to be transferred at the same time, as opposed to HTTP/1.x which only accepts one single request at a time.
HTTP/2 is backwards compatible with HTTP/1.1 and it’s supported by all modern browsers, so it comes with a ‘free’ performance improvement if your server supports it.
A Final Word About Your Web API and UX: Make Good Decisions and Test
A Web API that performs as fast as it gets, making efficient use of network bandwidth, memory, and processor, is the result of a number of good decisions. The decision-making process takes place not just during the early design stage but also goes on during development and subsequent phases of a software development project.
Conducting load/stress tests on an API and examining its results can help spot areas that demand better performance. Prioritize them according to their impact and design a plan that involves one or more action items addressing specific performance issues.
Back-end development is meant to serve the app that the user is interacting with, so the time it takes to deliver data can either help achieve a great UX or do the opposite.