Skip to main content

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 Room Manager) 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

  1. Log in to Fishjam Dashboard
  2. Create or select your Production environment (not Sandbox)
  3. Copy your production Fishjam URL and Management Token
  4. 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 Room Manager calls

Remove any Room Manager dependencies from your client code:

// ❌ Remove: Room Manager calls const response = await fetch( `https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}/room-manager?...`, ); // ✅ Replace with: Your authenticated API const response = await fetch("/api/join-room", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${userToken}`, }, body: JSON.stringify({ roomName }), });

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:

const newToken = fishjamClient.refreshPeerToken(roomId, peerId);

See also

For scaling considerations:

For specific backend frameworks: