Chat has become a necessary feature in most products. Businesses want to integrate quick, real-time communication to connect with their users. It can be for HR team management, hiring, onboarding, or within existing teams – but chat is almost everywhere.
If you are a developer or product manager looking to integrate a chat API into your application, this blog is for you. We will create a fully functional chat app with broadcast, direct message, and bot response functionalities using AWS services and test it using Postman. We will also check the out-of-the-box solutions.
Technical Considerations and Architecture
Chat App Components
At its core, real-time chat needs the following components:
- Database – Store the user context and chats
- APIs – WebSocket APIs for persistent connections
- Compute – To process messages, apply logic, and integrate with other services.
- Bot – For predefined responses or generative AI-powered interactions.
Other peripheral (but essential) components include –
- Authentication – To securely identify users and manage their sessions.
- Monitoring tools – To track application health, performance, and usage trends.
- AI Moderation – To filter out inappropriate content automatically.
In this article, we will implement the core functions of the chat app using AWS.
AWS Services Selection
This section will discuss which AWS services we will use and why to create a chat app.
- Amazon DynamoDB – Provides highly scalable, low-latency storage for user contexts and chat messages. Due to its fast read/write capabilities, it is suitable for real-time applications.
- Amazon API Gateway (WebSocket APIs) – This service enables persistent real-time connections for chat messages and scales automatically with user connections.
- AWS Lambda – Processes incoming messages, handles bot integration, and applies business logic. The serverless nature reduces operational overhead.
- Amazon Lex – Adds AI-powered bot functionality for predefined responses or generative interactions. It easily integrates with other AWS services.
These services integrate seamlessly to run chat applications; however, to achieve production-grade functionality, you will need additional services: Amazon Cognito, AWS CloudWatch, SQS, etc.
Architecture Diagram
This diagram provides the services integration and flow. Services in blur are not implemented here, but are added for further development reference.
![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/f62ad72625f458b4e9d5b4460de156bb/image1.png?auto=format&auto=compress)
Implementation Steps
Setting Up Services
DynamoDB
We will create two tables in DynamoDB, one to store connection details and the other for chat history. All the settings are used as default.
- ChatConnections: Stores active WebSocket connections and user information.
- Table Name: ChatConnections
- Partition Key: connectionId (String)
- ChatHistory: Stores chat messages with timestamps.
- Table Name: CHAT_HISTORY_TABLE
- Partition Key: chatId (String)
- Sort Key: timestamp (String)
AWS Lambda
We will create three lambda functions – connect, disconnect, and sendMessage
- Connect Handler: Registers new WebSocket connections.
- Environment Variables: WEBSOCKET_TABLE
- Required Permissions: dynamodb:PutItem
Python code: This code registers a new WebSocket connection and stores the connectionId and user details in the ChatConnections DynamoDB table. Copy the code to the connect.py lambda function you create and deploy the function.
1234567891011121314151617181920212223import json import boto3 import os dynamodb = boto3.client('dynamodb') def lambda_handler(event, context): connection_id = event['requestContext']['connectionId'] username = "Anonymous" # Default username if not registered # Save connection details in WEBSOCKET_TABLE dynamodb.put_item( TableName=os.environ['WEBSOCKET_TABLE'], Item={ 'connectionId': {'S': connection_id}, 'username': {'S': username} } ) return { "statusCode": 200, "body": "Connection established" }
- Disconnect Handler: Cleans up connection data when a user disconnects.
- Environment Variables: WEBSOCKET_TABLE
- Required Permissions: dynamodb:DeleteItem
- Python code: This code cleans up the connectionId from the ChatConnections table when a user disconnects. Copy the code to the disconnect.py lambda function and deploy it.
1234567891011121314151617181920def lambda_handler(event, context): connection_id = event['requestContext']['connectionId'] # Fetch username before deleting response = dynamodb.get_item( TableName=os.environ['WEBSOCKET_TABLE'], Key={'connectionId': {'S': connection_id}} ) username = response.get('Item', {}).get('username', {}).get('S', 'Unknown User') # Broadcast disconnection message broadcast_message(f"{username} has left the chat.", event) # Delete connectionId dynamodb.delete_item( TableName=os.environ['WEBSOCKET_TABLE'], Key={'connectionId': {'S': connection_id}} ) return {}
- Message Handler: Processes messages, handles broadcasting, direct messages, and bot responses.
- Environment Variables: WEBSOCKET_TABLE, CHAT_HISTORY_TABLE, LEX_BOT_ID, LEX_BOT_ALIAS_ID
- Required Permissions: dynamodb:PutItem, dynamodb:Scan, lex:RecognizeText
- Python code: Processes incoming messages for group broadcasts, direct messages, or bot interactions, and saves all messages to the chat_history table. Copy the code to sendMessage.py lambda and deploy it to test the broadcast feature.
Note: The following code implements the broadcast feature only. Please check this github repo for the complete code with register_user, direct_message and Lex bot interaction features.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162import json import boto3 import os import datetime dynamodb = boto3.client('dynamodb') def lambda_handler(event, context): body = json.loads(event['body']) message = body.get('message', '') action_type = body.get('type', '').strip() # New key for internal logic connection_id = event['requestContext']['connectionId'] connectionIds = [] # Initialize API Gateway management client apigatewaymanagementapi = boto3.client( 'apigatewaymanagementapi', endpoint_url="https://" + event["requestContext"]["domainName"] + "/" + event["requestContext"]["stage"] ) # Get all connection IDs for broadcasting the message response = dynamodb.scan(TableName=os.environ['WEBSOCKET_TABLE']) connectionIds = response.get('Items', []) # Handle "sendMessage" action if action_type == "sendMessage": # Save group message to chat history save_chat_message("group", message) broadcast_message(message, apigatewaymanagementapi, connectionIds) return {} # "register", "directMessage", "bot" actions are not implemented here. # Please check https://github.com/pratiksk/aws_chat_app for full implementation unimplemented_actions = ["register", "directMessage", "bot"] if action_type in unimplemented_actions: apigatewaymanagementapi.post_to_connection( Data=f"The '{action_type}' action is not implemented in this version. " f"Please check the full implementation here: https://github.com/pratiksk/aws_chat_app", ConnectionId=connection_id ) return {} def broadcast\_message(message, apigatewaymanagementapi, connectionIds): for connectionId in connectionIds: try: apigatewaymanagementapi.post\_to\_connection( Data\=message, ConnectionId\=connectionId\['connectionId'\]\['S'\] ) except Exception as e: print(f"Error sending message to {connectionId\['connectionId'\]\['S'\]}: {str(e)}") def save\_chat\_message(chat\_id, message): """Save chat messages to the DynamoDB chat history table.""" dynamodb.put\_item( TableName\=os.environ\['CHAT\_HISTORY\_TABLE'\], Item\={ 'chatId': {'S': chat\_id}, 'timestamp': {'S': datetime.datetime.utcnow().isoformat()}, 'message': {'S': message} } )
API Gateway
API Gateway provides the WebSocket API for persistent, real-time connections. Use the following steps to create API.
- Create a WebSocket API
- In the API Gateway console, create a new WebSocket API.
- Set the Route Selection Expression to $request.body.action.
- Define Routes
- $connect: Handles new WebSocket connections.
- $disconnect: Handles WebSocket disconnections.
- sendMessage: Processes and routes chat messages.
- Assign Lambda integrations to each route:
- $connect: Connect Handler Lambda function.
- $disconnect: Disconnect Handler Lambda function.
- sendMessage: Message Handler Lambda function.
- Deploy the API
- Create a deployment for the API in a stage (e.g., dev or prod).
- Note the WebSocket URL, which will be used by clients to connect (and we will use for testing).
- Set IAM Permissions
- Grant API Gateway permissions to invoke the Lambda functions by adding the IAM role to the AWSLambdaRole policy. This will be automatically created in the default setup.
Amazon LEX
We’ll use one of the example bots provided by Amazon LEX. An example bot has predefined questions and responses. Amazon LEX can be used in many ways to enhance the user experience, including voice and GenAI chat, which are not part of this implementation. Use the following steps to setup
- In the Amazon Lex V2 console, choose Traditional creation method and select Start with an example.
- Select an example bot like BookHotel for demonstration purposes.
- Configure the following:
- For voice-related features, go to the bottom and select none.
- Select a language.
- Create an alias (e.g., Prod)
- Publish the bot and attach it to the alias you created earlier.
- Note botId, botAliasId, and localeId from respective pages
Testing the Setup
Following are the steps to test the setup on Postman (you can use any tool of your choice)
- Establishing WebSocket connection
- Open Postman and create a new WebSocket request.
- Enter the WebSocket URL for your API Gateway.
- Create multiple connections to test chat.
- Sending and receiving messages – send the following payloads to test respective functionalities.
- Broadcast message to every connected user
12345{ "action": "sendMessage", "type": "sendMessage", "message": "Hello, everyone!" }
- Register a user - this is required to send direct messages. Register multiple users in different windows and keep windows open while testing for direct message functionality.
12345{ "action": "sendMessage", "type": "sendMessage", "message": "Hello, everyone!" }
- Direct Message
123456{ "action": "sendMessage", "type": "directMessage", "to": "User123", "message": "Hi, User123!" }
- Bot interaction
12345{ "action": "sendMessage", "type": "bot", "message": "Book hotel" }
- Verifying data storage in DynamoDB.
- Go to the console and check using the explore items option on the left side panel.
Scaling and Operational Challenges
While this app will work as expected using the above setup, taking it to production is a different ball game! Maintaining performance and scalability with optimal cost, security, and chat moderation are endless processes.
- Performance and Scalability: As user activity grows, ensuring low-latency message delivery and managing spikes in traffic require advanced strategies like caching, partitioning, and pre-warmed compute resources. Monitoring and adapting to usage patterns become critical.
- Cost Management: Real-time systems can be expensive, especially with unpredictable traffic. Balancing performance with budget constraints requires dynamic scaling, resource optimization, and constant fine-tuning.
- Security: Protecting user data and preventing abuse is a requirement for secure chat applications. Implementing secure WebSocket connections, authentication mechanisms, and user access controls adds to the operational complexity.
- Content Moderation: Ensuring a safe and engaging chat environment often involves integrating AI-driven moderation to filter inappropriate content and manage spam.
Each of these challenges demands significant engineering effort and operational oversight, making production-grade chat infrastructure a continuous, resource-intensive process.
Exploring Out-of-the-Box Solution
To bypass this effort and focus on your core business, consider integrating getstream APIs and SDKs. It can drastically reduce development and operational overhead with predictable costs.
Stream provides world-class real-time communication components that can be used across different platforms. It provides a modern chat interface with features such as reactions, threaded replies, URL enrichment, and more.
The comprehensive documentation and tutorials for SDKs for different frameworks and backend platforms can help you get started in minutes instead of weeks. You can also build custom UIs to provide your desired user experience. Plus, you can start coding for free.