Fast data exchange if often a necessity in web applications. But how to choose between Polling and WebSockets? Understanding the nuances between these two communication methods is important, as they both come with their own advantages and gotchas.
This post aims to compare Polling and WebSockets, providing insights to help you decide which approach best suits your application's needs.
As always: we’ve also provided a simple example app that you can deploy into your own AWS account with SST. It will bootstrap an Amazon API WebSockets Gateway, a few Lambda functions and a Next.js frontend to showcase real-time communication via WebSockets.
Now, let's dive deeper into the basics of Polling and WebSockets, how they work, and which solution is best for different approaches.
Understanding Polling
Polling is a technique where the client repeatedly requests data from the server at regular intervals. This method is often used to check for updates or changes in data.
There are two main types of Polling:
⚡️ Short Polling: The client sends requests to the server at fixed, short intervals. This can lead to unnecessary requests if the data hasn't changed, resulting in increased server load and bandwidth usage.
🐌 Long Polling: The client requests data from the server, and if no new data is available, the server holds the request open until new data is available or a timeout occurs. This reduces the number of requests compared to short polling and can be more efficient in terms of server load.
Both of these approaches can be used with SQS, for example, and can significantly affect your monthly charges.
Advantages of Polling include:
📚 Simplicity: Polling is straightforward to implement and understand, making it accessible for developers. This is perhaps the most important advantage that should not be overlooked in any way, especially considering how complex distributed systems have become over the years.
🔀 Compatibility: It works with any web technology or stack and does not require any special protocols or configurations.
But now to the drawbacks:
🔴 Inefficiency: Short polling can cause many unnecessary requests, using up bandwidth and server capacity. For small-scale applications, this might not be important, but as the scale grows, it can become a real bottleneck.
🐢 Latency: There can be a delay in receiving updates, especially with short polling, as the client only checks for new data at set intervals. This can result in a less responsive user experience compared to real-time communication methods like WebSockets. From many case studies, we've learned that the threshold for latency in customer satisfaction is very low. For more details, check out the DynamoDB whitepaper, where Amazon explains how even a small increase in latency can significantly impact sales.
In summary, polling is simple and easy to use, but it can create potential bottlenecks and does not provide real-time communication.
Exploring WebSockets
WebSockets are a communication protocol that provides full-duplex communication channels over a single, long-lived connection between a client and a server.
This means that both the client and server can send and receive messages simultaneously, allowing for real-time data exchange.
With WebSockets, data can flow freely in both directions without the need for repeated HTTP requests. This is achieved through a single handshake process, after which the connection remains open, enabling continuous data exchange.
Benefits of WebSockets include:
⚡️ Real-time Data: WebSockets allow for instant data transmission, making them ideal for applications that require real-time updates, such as chat applications, live sports scores, or online gaming.
🟢 Efficiency: By maintaining a single open connection, WebSockets reduce the overhead associated with establishing multiple HTTP connections, leading to more efficient use of resources and lower latency.
Potential challenges are mainly about 📚 Complexity. Implementing WebSockets is more complex than simple HTTP polling. It requires extra work for managing connections and handling errors. Even with helpful tools like Amazon API WebSocket Gateway, setting up WebSockets for real-time communication is not an easy task.
Very rarely you could potentially fight with Compatibility Issues. While WebSockets are widely supported in modern browsers, there may still be issues with very old browsers or certain network configurations that do not support WebSocket connections.
Amazon API Gateway Support
Amazon API Gateway supports both Polling (REST or HTTP Gateway) and WebSockets, so you can choose what fits best for you.
For Polling, API Gateway can be used to create RESTful APIs that clients can call at regular intervals to check for updates. This is suitable for applications where real-time updates are not critical, and the simplicity of implementation is a priority (that should always be a priority!). A common use case within AWS for polling is integrating with services like Amazon SQS, where a client periodically checks for new messages in a queue.
For WebSockets, API Gateway provides a WebSocket API that enables real-time, two-way communication between clients and servers. This is ideal for applications that require instant data updates, such as chat applications, live notifications, or collaborative tools.
Practical Implementation Tips
With frameworks like SST, it's very easy to start using WebSockets on AWS. The first step is to set up a new gateway with just a single line of configuration:
const webSocketApi = new sst.aws.ApiGatewayWebSocket('ws-api');
SST will handle the heavy lifting of setting up everything we need. After that, we need to connect the necessary connection management routes to a function:
webSocketApi.route('$connect', {
handler: 'functions/connections.handler',
});
webSocketApi.route('$disconnect', {
handler: 'functions/connections.handler',
});
webSocketApi.route('$default', {
handler: 'functions/connections.handler',
});
The connections.handler
will be packaged into a Lambda function, and SST will link the actions to the function. This means $connect
is triggered when a new client opens a connection to our WebSockets API Gateway, and $disconnect
is triggered if the connection is lost due to network errors or if the client decides to leave.
Even though Amazon API Gateway manages the connection, we need to store the connection data ourselves. Otherwise, we won't know where to send messages! To do this, we need a DynamoDB table:
const table = new sst.aws.Dynamo('connection', {
fields: {
connectionId: 'string',
},
primaryIndex: { hashKey: 'connectionId' },
});
Our hash key will be the connection identifier itself, which will be provided to us by the API Gateway. Let's connect the table to our functions:
webSocketApi.route('$connect', {
handler: 'functions/connections.handler',
link: [table],
});
webSocketApi.route('$disconnect', {
handler: 'functions/connections.handler',
link: [table],
});
webSocketApi.route('$default', {
handler: 'functions/connections.handler',
link: [table],
});
The last thing we need is the necessary IAM permissions and the connection management URL endpoint.
const environment = {
WEBSOCKET_URL: webSocketApi.managementEndpoint,
};
const permissions = [
{
actions: ['execute-api:ManageConnections'],
resources: [
`arn:aws:execute-api:*:*:*/$default/POST/@connections/{connectionId}`,
],
},
{
actions: ['execute-api:Invoke'],
resources: ['*'],
},
];
Let's add these to our functions in the permissions
and environment
fields:
webSocketApi.route('$connect', {
handler: 'functions/connections.handler',
environment,
permissions,
link: [table],
});
webSocketApi.route('$disconnect', {
handler: 'functions/connections.handler',
environment,
permissions,
link: [table],
});
webSocketApi.route('$default', {
handler: 'functions/connections.handler',
environment,
permissions,
link: [table],
});
That's it! We're all set up. In our connections.ts
, we can now easily route based on the action
to either store a new connection in DynamoDB or delete an existing one.
export const handler: APIGatewayProxyHandler = async (event) => {
const { connectionId, routeKey } = event.requestContext;
if (!connectionId) {
return { statusCode: 400, body: 'Missing connectionId' };
}
if (routeKey === '$connect') {
await saveConnection(connectionId);
} else if (routeKey === '$disconnect') {
await deleteConnection(connectionId);
} else {
return { statusCode: 400, body: 'Unsupported route' };
}
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
};
In the final step, we can create a custom action, such as send
, where we accept WebSocket messages sent from one client to another. This action can also be directly linked to our Lambda function using SST.
In our frontend, we can establish a new WebSocket connection using the environment variable we passed:
const newSocket = new WebSocket(process.env.NEXT_PUBLIC_WEBSOCKET_URL);
We'll also listen for all events. This way, we'll know when the connection is established and when a new message is sent to our client:
newSocket.onopen = () => {
console.log('WebSocket connection established');
setSocket(newSocket);
};
newSocket.onmessage = (event) => {
console.log('Received message:', event.data);
const data: Message = JSON.parse(event.data);
if (data.message) {
toast(`${data.message} from ${data.from}!`, toastStyle);
}
if (data.connectionId) {
console.info(`Own connectionId: ${data.connectionId}!`);
setConnectionId(data.connectionId);
}
};
We can also send new messages using our stored socket
variable.
In the backend, we can directly send messages from the server to the client using the AWS SDK:
const apiGateway = new ApiGatewayManagementApi({
endpoint: process.env.WEBSOCKET_URL,
});
await apiGateway
.postToConnection({
ConnectionId: `$TARGET_ID`,
Data: JSON.stringify({ message, from: connectionId, to: targetId }),
})
.promise();
We can pass any data here; we just need to ensure that the clients expect the same format.
That’s it! 🎉
Conclusion
Polling and WebSockets are two different ways to handle data exchange in web apps, each with its pros and cons.
Polling is straightforward and works with any web tech, making it easy to set up. However, it can be inefficient and cause delays, especially in big applications.
WebSockets, on the other hand, provide real-time data transfer and efficiency by keeping a single connection open, but they can be more complex to implement.
The right choice depends on what your app needs. If you need real-time updates, WebSockets might be the way to go. For simpler apps where real-time isn't a priority, Polling could work just fine.
Try both methods with Amazon API Gateway to see which suits your needs best!
FAQ
What is the main difference between Polling and WebSockets?
Polling involves the client repeatedly requesting data from the server at regular intervals, which can lead to inefficiencies and latency. WebSockets, on the other hand, provide a full-duplex communication channel that allows real-time data exchange between the client and server over a single, long-lived connection.
When should I use Polling over WebSockets?
Polling is suitable for applications where real-time updates are not critical, and simplicity is a priority. It works well for smaller applications or scenarios where the data does not change frequently, and the overhead of maintaining a WebSocket connection is not justified.
What are the benefits of using WebSockets?
WebSockets offer real-time data transmission, reduced latency, and efficient use of resources by maintaining a single open connection. They are ideal for applications that require instant updates, such as chat applications, live notifications, or collaborative tools.
Can I use Amazon API Gateway for both Polling and WebSockets?
Yes, Amazon API Gateway supports both Polling (via REST or HTTP Gateway) and WebSockets. This allows you to choose the method that best fits your application's requirements, whether you need real-time communication or periodic data updates.
How can I experiment with Polling and WebSockets using Amazon API Gateway?
You can start by deploying a simple example app that uses Amazon API Gateway to set up both Polling and WebSockets. This will help you understand the implementation details and evaluate which method works best for your specific use case.