Deploying a Real-Time Chat App on AWS Lightsail Containers | Next.js & WebSocket Backend
AWS Lightsail Container Solution
GitHub Repo: https://github.com/prafulpatel16/realtime- chatapp-test.git
Video Link: https://youtu.be/kVH8n5w6Gkw
Real-Time Use Case Solution: Real-Time Chat Application for Customer Support
Solution Overview:
To solve this problem, the company deploys a real-time chat application that integrates directly into their e-commerce website. The application consists of:
Next.js Frontend for the chat interface, allowing customers to engage with support agents or AI-driven chatbots.
WebSocket Backend to handle bi-directional, real-time communication between customers and the support team.
Dockerized Containers deployed on AWS Lightsail to ensure scalability and performance.
Architecture and Workflow
Components:
Frontend (Next.js): A user-friendly chat widget embedded in the e-commerce site where customers can enter their inquiries. It allows real-time messaging with the support backend.
Backend (WebSocket Server): Handles real-time communication between the frontend chat widget and either support agents or AI bots. All communication is managed through WebSocket connections, ensuring real-time message delivery without delays.
Docker Containers on AWS Lightsail: The frontend and backend are deployed as separate Docker containers on AWS Lightsail, ensuring high availability, security, and cost-effectiveness.
Real-Time Workflow:
Customer Initiates Chat: A customer browsing the e-commerce site initiates a chat session from the embedded chat widget.
Connection Established: The Next.js frontend establishes a WebSocket connection to the backend running on AWS Lightsail.
Real-Time Messaging: The customer’s queries are sent to the backend in real-time and are either directed to a human support agent or handled by an AI-driven chatbot.
Instant Responses: The customer receives an instant response, reducing wait time from hours (via email) to seconds. Support agents can handle multiple conversations simultaneously, further improving efficiency.
Scalability: During peak times, AWS Lightsail scales the container services to handle additional chat sessions without downtime.
Key Features and Benefits
Instant Response Time: Real-time communication ensures that customer queries are answered immediately, leading to higher customer satisfaction.
Scalable Solution: The system can scale to support thousands of concurrent users, especially during peak hours or events, using the elasticity of AWS Lightsail.
Seamless User Experience: Customers can communicate with agents without leaving the website, providing a seamless experience that keeps them engaged and more likely to complete purchases.
Cost Efficiency: By deploying in lightweight Docker containers on AWS Lightsail, the company minimizes infrastructure costs while maintaining high performance.
Operational Efficiency: The chat system enables support agents to handle multiple customers in real time, reducing the need for hiring more agents.
Technical Stack
Frontend: Next.js for chat UI
Backend: WebSocket server in Node.js
Containerization: Docker
Cloud Platform: AWS Lightsail for cost-effective container deployment
Scalability: Autoscaling support during high-traffic periods
Security: SSL/TLS configuration for secure WebSocket communication
The real-time chat allows customers to receive instant answers to their queries, reducing wait times significantly and increasing customer satisfaction. The system is designed to scale dynamically during peak times, ensuring smooth operations during high-traffic periods such as sales.
Step 1: Initialize the Project
Create a new directory for the chat application and initialize both Next.js and the WebSocket server.
mkdir realtime-chat-app
cd realtime-chat-app
### 1.1. Next.js Frontend Setup
First, let's create the Next.js frontend.
npx create-next-app@latest frontend
You can go through the prompts or use flags to set up the defaults:
npx create-next-app@latest frontend --ts --eslint --src-dir --tailwin
This command sets up a TypeScript Next.js project with ESLint, `src` directory structure, and TailwindCSS for styling (optional, but highly recommended).
### 1.2. WebSocket Backend Setup
Next, let's set up the backend in a `backend` folder, which will host the WebSocket server.
mkdir backend
cd backend
npm init -y
npm install ws express cors dotenv
The `ws` package is for WebSocket implementation, `express` is for running an HTTP server, and `dotenv` is for environment variables.
### Step 2: Directory Structure
Here is the full directory structure for the app:
realtime-chat-app/
│
├── backend/
│ ├── server.js # WebSocket backend server
│ ├── package.json # Node.js dependencies
│ └── .env # Environment variables for WebSocket server
│
├── frontend/
│ ├── public/ # Static files
│ ├── src/ # Application logic
│ │ ├── components/ # React components for chat UI
│ │ ├── pages/ # Next.js pages
│ │ ├── utils/ # WebSocket connection utility
│ ├── package.json # Next.js dependencies
│ ├── tailwind.config.js # TailwindCSS configuration (optional)
│ └── .env.local # Frontend environment variables
└── README.md # Project documentation
```
### Step 3: Backend WebSocket Server Setup
Inside the `backend` folder, create a file `server.js`:
```javascript
// backend/server.js
const express = require('express');
const { Server } = require('ws');
const cors = require('cors');
require('dotenv').config();
const app = express();
app.use(cors());
const PORT = process.env.PORT || 8080;
// HTTP server for serving WebSocket on the same port
const server = app.listen(PORT, () => {
console.log(`WebSocket server running on port ${PORT}`);
});
// WebSocket server
const wss = new Server({ server });
wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
console.log(`Received: ${message}`);
// Broadcasting the message to all connected clients
wss.clients.forEach((client) => {
if (client.readyState === ws.OPEN) {
client.send(message);
}
});
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
```
Create a `.env` file for the backend:
```plaintext
# backend/.env
PORT=8080
```
### Step 4: Next.js Frontend Setup
Go to the `frontend` directory and update `src/pages/index.tsx` to use WebSocket.
Here’s how the basic chat interface will look:
```tsx
// frontend/src/pages/index.tsx
import { useState, useEffect } from 'react';
const ChatPage = () => {
const [messages, setMessages] = useState<string[]>([]);
const [input, setInput] = useState('');
const [ws, setWs] = useState<WebSocket | null>(null);
useEffect(() => {
const socket = new WebSocket(process.env.NEXT_PUBLIC_WS_URL as string);
setWs(socket);
socket.onmessage = (event) => {
const newMessage = event.data;
setMessages((prevMessages) => [...prevMessages, newMessage]);
};
socket.onclose = () => {
console.log('WebSocket connection closed');
};
return () => {
socket.close();
};
}, []);
const sendMessage = () => {
if (ws && input.trim()) {
ws.send(input);
setInput('');
}
};
return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-bold">Chat Room</h1>
<div className="border rounded p-2 h-64 overflow-y-scroll">
{messages.map((message, index) => (
<div key={index} className="py-1">{message}</div>
))}
</div>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
className="border p-2 rounded w-full my-2"
placeholder="Type a message..."
/>
<button
onClick={sendMessage}
className="bg-blue-500 text-white p-2 rounded"
>
Send
</button>
</div>
);
};
export default ChatPage;
```
This `ChatPage` component listens to WebSocket messages and renders them in a chat box. Users can type a message and send it, which will be broadcast to all connected clients via the WebSocket server.
### Step 5: Environment Variables
In the `frontend` folder, create a `.env.local` file:
```plaintext
NEXT_PUBLIC_WS_URL=ws://localhost:8080
```
### Step 6: Tailwind CSS Setup (Optional)
If you chose to use Tailwind, add it to the frontend project:
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
```
Update `tailwind.config.js`:
```javascript
// frontend/tailwind.config.js
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
```
Then, add the following to your `src/styles/globals.css`:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
### Step 7: Run the Application
#### 7.1. Start WebSocket Backend
```bash
cd backend
npm start
```
#### 7.2. Start Next.js Frontend
```bash
cd frontend
npm run dev
```
The frontend will be available at `http://localhost:3000` and the WebSocket server at `ws://localhost:8080`.
### Step 8: Testing Routes and Final App
When you visit `http://localhost:3000`, you should see a chat interface. Messages sent will appear for all connected clients, thanks to the WebSocket setup.
### Final Routes
1. **Frontend Route**:
- `/` (Chat interface)
2. **WebSocket Backend Route**:
- `ws://localhost:8080` (WebSocket connection endpoint)
With these steps, you have a fully working chat application with a real-time WebSocket backend and Next.js frontend.
Step 7: Run the Application
7.1. Start WebSocket Backend
cd backend
npm start
7.2. Start Next.js Frontend
cd frontend
npm run dev
frontend
.env
NEXT_PUBLIC_WS_URL=ws://localhost:8080
backend
.env
# backend/.env
PORT=8080
# docker-compose.yml version: '3' services: websocket-backend: build: ./backend ports: - "8080:8080" environment: - PORT=8080 restart: always nextjs-frontend: build: ./frontend ports: - "3000:3000" environment: - NEXT_PUBLIC_WS_URL=ws://websocket-backend:8080 depends_on: - websocket-backend restart: always
docker-compose up --build
open frontend web app url
Deploy docker containers on AWS Lightsail containers
Prerequisites:
AWS CLI Installed: Ensure you have the AWS CLI installed. You can verify this by running:
aws --version If it’s not installed, follow the AWS CLI installation guide.
AWS CLI Configured: Make sure the AWS CLI is configured with valid credentials. If not, configure it withaws configure
You’ll need to provide your AWS Access Key ID, Secret Access Key, Region, and Output format (e.g.,
json
).
Steps to Create an ECR Repository:
Create the ECR Repository: You can create an ECR repository using the following AWS CLI command:
ecr create-repository --repository-name <repository-name> --region <region> Replace <repository-name> with the name of your repository and <region> with the AWS region where you want to create it. For example:
ecr create-repository --repository-name my-realtime-chatapp --region us-east-1
Example Output: Upon successful creation, you should get a response like this:
jsonCopy code{ "repository": { "repositoryArn": "arn:aws:ecr:us-east-1:123456789012:repository/my-realtime-chatapp", "registryId": "123456789012", "repositoryName": "my-realtime-chatapp", "repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-realtime-chatapp", "createdAt": 1644870241.0, "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": false }, "encryptionConfiguration": { "encryptionType": "AES256" } } }
Steps to Push Docker Images to ECR:
1. Authenticate Docker to Your ECR Registry
Before pushing images to ECR, you need to authenticate Docker to your Amazon ECR registry.
Run this command to authenticate (replace
<your-region>
with your AWS region, e.g.,us-east-1
):aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.<your-region>.amazonaws.com
For example, if your AWS region is
us-east-1
and your account ID is123456789012
, the command would be:aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
Amazon ECR repositories use a specific URL format for tagging images, which includes your AWS account ID, region, and repository name.
You will need to tag both images (
realtime-chatapp-test-nextjs-frontend
andrealtime-chatapp-test-websocket-backend
) so they point to the correct ECR repository.Tag the Frontend Image:
docker tag realtime-chatapp-test-nextjs-frontend:latest <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-realtime-chatapp:frontend-latest
Example:
docker tag realtime-chatapp-test-nextjs-frontend:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-realtime-chatapp:frontend-latest
Tag the Backend Imagedocker tag realtime-chatapp-test-websocket-backend:latest <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-realtime-chatapp:backend-..
Example:
bdocker tag realtime-chatapp-test-websocket-backend:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-realtime-chatapp:backend-latest
3. Push the Docker Images to ECR
Now that the images are tagged, you can push them to the ECR repository.
Push the Frontend Image:
docker push <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-realtime-chatapp:frontend-latest
Example:
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-realtime-chatapp:frontend-latest
Push the Backend Image:
docker push <your-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-realtime-chatapp:backend-latest
Example:
bashCopy codedocker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-realtime-chatapp:backend-latest
Verify Your Images in ECR:
Once the push is successful, you can verify that your images have been uploaded by going to the Amazon ECR Console and checking the
my-realtime-chatapp
repository.
When configuring a WebSocket connection on AWS Lightsail or any other environment, WebSockets generally use either:
ws://
for unencrypted WebSocket connectionswss://
for encrypted WebSocket connections (WebSockets over TLS/SSL, similar to HTTPS).
Which Protocol to Use:
ws://
(Unencrypted WebSocket):This is used for local development or when you don’t have SSL/TLS certificates configured on your server.
Example:
ws://
localhost:8080
wss://
(Secure WebSocket):This is the recommended protocol for production environments, including AWS Lightsail.
WebSocket connections secured with SSL/TLS (just like HTTPS).
Example:
wss://
your-domain.com/websocket
Deploy on AWS Lighsail
Create IAM Role:
EC2-Lightsail-ECR-Route53-Role
Create a Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:DeleteServiceLinkedRole",
"iam:GetServiceLinkedRoleDeletionStatus"
],
"Resource": "arn:aws:iam::*:role/aws-service-role/lightsail.amazonaws.com/AWSServiceRoleForLightsail*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CopySnapshot",
"ec2:DescribeSnapshots",
"ec2:CopyImage",
"ec2:DescribeImages"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetAccountPublicAccessBlock"
],
"Resource": "*"
}
]
}
Setup AWS Lightsail
-
Sign in with your AWS credentials.
Create a Lightsail Container Service:
Navigate to the Containers section.
Click on Create a container service.
Choose the size of the container based on your application's resource needs (start with a smaller plan if unsure).
Select the AWS region closest to your users.
Deploy Frontend and Backend Containers
Deploy Backend Container:
After creating the container service, select it and click Add container.
Under Container Name, enter a name like
backend
.Set the Container image to your backend image (e.g.,
<your-username>/realtime-chat-backend:latest
).Set Open ports to
8080
(the port your WebSocket server listens on).Click Save.
Deploy Frontend Container:
Repeat the above steps for your frontend:
Add a container named
frontend
.Set the Container image to your frontend image (e.g.,
<your-username>/realtime-chat-frontend:latest
).Set Open ports to
3000
(the port your Next.js app listens on).
Click Save and Deploy.
Deploy both the containers within one Lightsail Container service
Access the Webapp and verify on the browser
Conclusion
By deploying a real-time chat application using Next.js, WebSockets, and AWS Lightsail containers, the e-commerce company solved their customer support issues, scaled their infrastructure efficiently, and significantly boosted both customer satisfaction and revenue. This real-time business solution is a powerful use case for modern e-commerce platforms looking to optimize user engagement and operational efficiency.