A standalone chat server built with FastAPI and Socket.IO that powers real-time chat functionality for a Laravel application. The server is designed as a simple message broker that receives events from Laravel and broadcasts them to connected clients.
- Real-time messaging with Socket.IO
- No authentication or database required
- Simple API endpoints for Laravel integration
- Conversation room management
- Typing indicators
- Message read receipts
- API key security for all endpoints
This repository contains:
socket_server.py
: The FastAPI Socket.IO serverconfig.py
: Configuration for the serverSocketClient.php
: Laravel service class for interacting with the serverMessageController.php
: Example Laravel controller using the SocketClienttest_client.js
: Node.js test client for testing the Socket.IO serverweb_client.html
: Browser-based client for testing the Socket.IO server
- Clone the repository:
git clone https://github.com/yourusername/tuneup-chat.git
cd tuneup-chat
- Install Python dependencies:
pip install fastapi uvicorn socketio python-socketio[asgi] python-dotenv
- Create a
.env
file in the root directory:
TUNEUP_API_KEY=your_secure_api_key_here
- Run the socket server:
python socket_server.py
The server will start at http://localhost:8001
by default.
- Clone the repository:
git clone https://github.com/yourusername/tuneup-chat.git
cd tuneup-chat
- Set your API key in the environment (optional):
export TUNEUP_API_KEY=your_secure_api_key_here
- Build and start the containers:
docker-compose up -d
This will start:
- The Socket.IO server at
http://localhost:8001
- A web server for the client at
http://localhost:3000
- View logs:
docker-compose logs -f
- Stop the containers:
docker-compose down
- Install Node.js dependencies:
npm install
- Run the test client:
node test_client.js 1 your_api_key_here
- If running without Docker, start a local web server:
npx http-server -p 3000
- Open in your browser:
- If using Docker:
http://localhost:3000/web_client.html
- If using local server:
http://localhost:3000/web_client.html
- If using Docker:
The server requires a valid API key for all requests. The API key must be provided in the X-Tuneup-API-Key
header for all HTTP requests and Socket.IO connections.
Make sure to:
- Store your API key in the
.env
file - Keep your API key secret
- Use a strong, randomly generated API key in production
- Configure the same API key in both your Laravel application and the Socket.IO server
Copy SocketClient.php
to your Laravel project's app/Services
directory.
In your AppServiceProvider.php
:
public function register()
{
$this->app->singleton(SocketClient::class, function ($app) {
return new SocketClient();
});
}
In config/services.php
:
'socket' => [
'url' => env('SOCKET_SERVER_URL', 'http://localhost:8001'),
'api_key' => env('TUNEUP_API_KEY', ''),
],
Then in your .env
file:
SOCKET_SERVER_URL=http://localhost:8001
TUNEUP_API_KEY=your_secure_api_key_here
See MessageController.php
for examples of how to use the SocketClient service to emit events.
The server exposes these main endpoints:
POST /emit
: General purpose endpoint to emit any eventPOST /send-message
: Send a message to a conversationPOST /typing-status
: Update a user's typing statusPOST /mark-read
: Mark messages as read
All endpoints require the X-Tuneup-API-Key
header.
Clients can listen for these events:
new_message
: Emitted when a new message is senttyping_status
: Emitted when a user starts or stops typingmessages_read
: Emitted when a user reads messages
In your JavaScript frontend, use the Socket.IO client to connect to the server and automatically join conversation rooms from Laravel:
// Import Socket.IO client
import { io } from "socket.io-client";
import axios from "axios";
// Connect to the Socket.IO server
const socket = io("http://localhost:8001", {
auth: {
id: userId, // User ID from Laravel auth
isStudent: true, // Or false for instructors
},
extraHeaders: {
"X-Tuneup-API-Key": "your_api_key_here", // API key for security
},
});
// Fetch conversations from Laravel backend and join rooms
async function joinConversationRooms() {
try {
// Get conversations from Laravel endpoint
const response = await axios.get("/api/conversations", {
headers: {
Authorization: `Bearer ${yourAuthToken}`, // Your Laravel auth token
},
});
const conversations = response.data.data;
// Join each conversation room
conversations.forEach((conversation) => {
socket.emit(
"join_conversation",
{ conversation_id: conversation.id },
(response) => {
console.log(`Joined conversation ${conversation.id}:`, response);
}
);
});
return conversations;
} catch (error) {
console.error("Failed to fetch conversations:", error);
return [];
}
}
// Call this function when your app initializes
const conversations = await joinConversationRooms();
// Set up event listeners for all conversations
socket.on("new_message", (message) => {
console.log("New message:", message);
// Check if message belongs to one of our conversations
if (conversations.some((conv) => conv.id === message.conversation_id)) {
// Update UI with new message
// For example: updateConversationMessages(message);
}
});
// Listen for typing status updates
socket.on("typing_status", (status) => {
console.log("Typing status:", status);
// Update typing indicator UI for the specific conversation
// For example: updateTypingIndicator(status.conversation_id, status.user_id, status.is_typing);
});
// Listen for message read updates
socket.on("messages_read", (status) => {
console.log("Messages read:", status);
// Update read receipts UI for the specific conversation
// For example: updateReadReceipts(status.conversation_id, status.user_id, status.message_ids);
});
// Rejoin rooms when socket reconnects
socket.on("connect", async () => {
console.log("Socket reconnected, rejoining rooms...");
await joinConversationRooms();
});
With this approach, your client will:
- Fetch conversations from your Laravel backend
- Automatically join Socket.IO rooms for each conversation
- Listen for events and handle them appropriately based on conversation context
- Reconnect and rejoin rooms if the connection is lost
For production use:
- Configure a proper web server (e.g., Nginx) in front of the Socket.IO server
- Set up proper CORS settings in
config.py
- Consider using a process manager like Supervisor to keep the server running
- Use HTTPS for all communication to prevent API key exposure
- Generate a strong, random API key and keep it secure