How to Deploy Fishjam to Production
How-to Guide - Deploy your Fishjam backend safely to production
This guide covers the essential steps to move from development (using the Sandbox API) to a production-ready Fishjam backend deployment.
Prerequisites
- Working Fishjam backend (see Backend Quick Start)
- Production Fishjam environment (not Sandbox)
- Domain and SSL certificates for your backend
- Production database and infrastructure
Step 1: Set up production Fishjam environment
Get production credentials
- Log in to Fishjam Dashboard
- Create or select your Production environment (not Sandbox)
- Copy your production Fishjam URL and Management Token
- Note: These are different from your sandbox credentials
Environment variables setup
Create production environment variables:
# Production Fishjam credentials FISHJAM_URL="your-production-fishjam-url" FISHJAM_MANAGEMENT_TOKEN="your-production-management-token" # Your application settings NODE_ENV="production" PORT="3000" DATABASE_URL="your-production-database-url" # Security settings JWT_SECRET="your-secure-jwt-secret" CORS_ORIGIN="https://yourdomain.com"
Step 2: Implement proper authentication
Replace Sandbox API calls
Remove any Sandbox API dependencies from your client code:
// ❌ Remove: Sandbox API calls const peerToken = await getSandboxPeerToken(roomName, userName); // ✅ Replace with: Your authenticated API const response = await fetch("https://your-backend.com/api/join-room", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${userToken}`, }, body: JSON.stringify({ roomName, userName }), });
Implement user authentication
const fishjamClient = new FishjamClient({ fishjamUrl: process.env.FISHJAM_URL!, managementToken: process.env.FISHJAM_MANAGEMENT_TOKEN!, }); // Authentication middleware const authenticateUser = async ( req: express.Request, res: express.Response, next: express.NextFunction, ) => { try { const token = req.headers.authorization?.replace("Bearer ", ""); if (!token) { return res.status(401).json({ error: "Authentication required" }); } const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await getUserById(decoded.userId); next(); } catch (error) { res.status(401).json({ error: "Invalid token" }); } }; // Protected endpoint for joining rooms app.post("/api/join-room", authenticateUser, async (req, res) => { try { const { roomName } = req.body; const user = req.user; // Create or get room const room = await getOrCreateRoom(roomName); // Verify user has permission to join this room if (!canUserJoinRoom(user, room)) { return res.status(403).json({ error: "Access denied" }); } // Create peer with user metadata const { peer, peerToken } = await fishjamClient.createPeer(room.id, { metadata: { userId: user.id, name: user.name, avatar: user.avatar, role: user.role, }, }); res.json({ peerToken, fishjamUrl: process.env.FISHJAM_URL, }); } catch (error) { console.error("Join room error:", error); res.status(500).json({ error: "Failed to join room" }); } });
(Optional) Step 3: Handle webhooks and events
You may wish to receive events from Fishjam regarding created rooms and peers. All you need for this is a single api endpoint:
Webhook endpoint
// Webhook signature verification const verifyWebhookSignature = ( req: express.Request, res: express.Response, next: express.NextFunction, ) => { const signature = req.headers["x-fishjam-signature"]; const payload = JSON.stringify(req.body); const expectedSignature = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(payload, "utf8") .digest("hex"); if (signature !== expectedSignature) { return res.status(401).json({ error: "Invalid signature" }); } next(); }; // Webhook handler app.post( "/api/webhooks/fishjam", express.raw({ type: "application/json" }), verifyWebhookSignature, (req: express.Request, res: express.Response) => { const event = req.body; switch (event.type) { case "peer_connected": handlePeerConnected(event.data); break; case "peer_disconnected": handlePeerDisconnected(event.data); break; case "room_empty": handleRoomEmpty(event.data); break; default: console.log("Unhandled event type:", event.type); } res.status(200).json({ received: true }); }, );
Enabling webhooks
Now, with your endpoint setup, all you need to do is supply your webhook endpoint to Fishjam when creating a room:
const createRoomWithWebhooks = async ( roomName: string, roomType = "conference", ) => { const room = await fishjamClient.createRoom({ roomType, webhookUrl: `${process.env.BASE_URL}/api/webhooks/fishjam`, }); return room; };
Common production issues
Issue: Token expiration handling
Peer tokens expire 24h after creation. We encourage keeping room and peer lifetimes as short as possible (typically a single room corresponds to a single video call or stream). However, if you wish to reuse a single peer over multiple days, you can make use of token refreshing:
- Node.js / TypeScript
- Python
const newToken = fishjamClient.refreshPeerToken(roomId, peerId);
new_token = fishjam_client.refresh_peer_token(room_id, peer_id)
See also
For scaling considerations:
For specific backend frameworks: