How to Upgrade Video Chat Application from Twilio to Zoom with Svelte and Go

September 03, 2024
Written by
Joseph Udonsak
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

How to Upgrade Video Chat Application from Twilio to Zoom with Svelte and Go

Hey there, great to have you again. A while back, I showed you how you can use Svelte and Go to build a video chat application with Twilio Video. However, Twilio Video is being discontinued, with End of Life (EOL) expected on December 5, 2024. Given that, current customers are being advised to migrate to Zoom Video SDK.

So, in this tutorial, I will walk you through the migration process, using the previous tutorial as a point of reference. At the end of this tutorial, you will have the same application, albeit with updated dependencies — and a video service with long term support.

Prerequisites

To follow this tutorial, you will need the following:

To get started, download a zip file with the previous application.

Upgrade the backend

The first step in the upgrade process is to update the environment variables, replacing the Twilio credentials with Zoom credentials. In the backend folder, create a new file named .env.local and add the following to it.

ZOOM_VIDEO_SDK_KEY="<<ZOOM_VIDEO_SDK_KEY>>"
ZOOM_VIDEO_SDK_SECRET="<<ZOOM_VIDEO_SDK_SECRET>>"
TOKEN_TTL=3600

To get your Zoom credentials, go to the Zoom App Marketplace, sign in with your Video SDK account, hover over Develop, and click Build Video SDK.

Zoom App Marketplace screen showing categories and a dropdown menu under the Develop button

Then, scroll down to the SDK credentials section to see your SDK key and secret.

Screenshot of the "Build SDK" page showing the "SDK credentials" section
Your SDK credentials are different from your API keys.

Then, update your .env.local file with the SDK Key and SDK Secret keys copied from the console.

After that, remove the Twilio Go Helper Library by running the following commands.

go get github.com/twilio/twilio-go@none

Like Twilio Video, Zoom’s Video SDK works with a JWT, which also has to be generated on the backend. To generate it, you will use the jwt-go package. Install it by running the following command.

go get -u github.com/golang-jwt/jwt/v5

Next, update the code in helper/token.go to match the following:

package helper
import (
	"crypto/md5"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"os"
	"strconv"
	"time"
	"github.com/golang-jwt/jwt/v5"
)
func GenerateToken(roomName string) (string, string) {
	zoomKey := os.Getenv("ZOOM_VIDEO_SDK_KEY")
	zoomSecret := os.Getenv("ZOOM_VIDEO_SDK_SECRET")
	currentTime := time.Now().Unix()
	tokenTTL, _ := strconv.ParseInt(os.Getenv("TOKEN_TTL"), 10, 0)
	expiry := currentTime + tokenTTL
	userIdentity := identity()
	token := jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		jwt.MapClaims{
			"app_key":       zoomKey,
			"role_type":     1,
			"version":       1,
			"tpc":           roomName,
			"user_identity": userIdentity,
			"iat":           currentTime,
			"nbf":           currentTime,
			"exp":           expiry,
		},
	)
	jwtToken, err := token.SignedString([]byte(zoomSecret))
	if err != nil {
		fmt.Println(err)
	}
	return jwtToken, userIdentity
}
func identity() string {
	input, _ := rand.Prime(rand.Reader, 128)
	hash := md5.Sum([]byte(input.String()))
	return hex.EncodeToString(hash[:])
}

There are two key changes in this new version of the code. First, the JWT generation is handled by go-jwt instead of twilio-go. Second, the user identity is returned in addition to the signed JWT. This is because you will need the identity on the frontend to join a video session.

Now, in main.go, in the project's top-level directory, update the roomHandler() function to match the following code.

func roomHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	response := make(map[string]string)
	var room model.Room
	json.NewDecoder(request.Body).Decode(&room)
	err := room.Validate()
	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		response["message"] = err.Error()
	} else {
		jwtToken, userIdentity := helper.GenerateToken(room.Name)
		response["jwt"] = jwtToken
		response["userName"] =userIdentity
	}
	jsonResponse, err := json.Marshal(response)
	if err != nil {
		log.Fatalf("Error happened in JSON marshal. Err: %s", err)
	}
	writer.Write(jsonResponse)
}

Since the GenerateToken() function now returns two values, an extra variable is added for the user identity. This is added to the response for use on the frontend.

And…that’s it for the backend! It's now up to date. So, start it by running the following command:

go run main.go

Update the frontend

Next, in a new terminal tab or session, head over to the frontend folder, in the project's top-level directory, to finish up.

Let's start with a bit of housekeeping. Migrate your code to the latest Svelte version with the following command:

npx svelte-migrate@latest svelte-4

Press the (y) key to proceed if you get the following message.

Need to install the following packages:
  svelte-migrate@latest
Ok to proceed? (y)

You will be asked a further series of questions, which you should answer as shown below.

✔ Continue? … yes
✔ Which folders should be migrated? › public, scripts, src
✔ Add the `|global` modifier to currently global transitions for backwards compatibility? More info at https://svelte.dev/docs/v4-migration-guide#transitions-are-local-by-default … n

Now, remove the twilio-video package by running the following command:

npm remove twilio-video

Then, install the Zoom Video Toolkit using the following command:

npm install @zoom/videosdk-ui-toolkit --save-dev

If you get any audit issues for outdated dependencies, you can fix them with the following command.

npm audit fix

Next, update the code in src/Helper.js, to match the following.

import axios from "axios";
import iziToast from "izitoast";
import uitoolkit from "@zoom/videosdk-ui-toolkit";

export const notify = (message, isError = false) => {
  const toastConfig = {
    message,
    position: "topRight",
  };
  if (isError) {
    iziToast.error(toastConfig);
  } else {
    iziToast.success(toastConfig);
  }
};

const axiosInstance = axios.create({
  baseURL: "http://localhost:8000",
});

const getAccessToken = async (roomName) => {
  try {
    const response = await axiosInstance.post("", { roomName });
    return response.data;
  } catch (e) {
    throw new Error(e.response.data.message);
  }
};

export const connectToRoom = async (
  sessionName,
  videoContainer,
  sessionClosedHandler
) => {
  const { jwt: videoSDKJWT, userName } = await getAccessToken(sessionName);
  const config = {
    videoSDKJWT,
    sessionName,
    userName,
    features: ["video", "audio", "share", "chat", "users", "settings"],
  };
  uitoolkit.joinSession(videoContainer, config);
  uitoolkit.onSessionClosed(sessionClosedHandler);
  notify(`Successfully joined Room: ${sessionName}`);
};

The revised connectToRoom() function replaces the previous implementation with the Zoom implementation. An extra parameter named sessionClosedHandler is added. This is a callback function used to update the application when the Zoom session ends. Also, the notify() function is enhanced to display error notifications and not just success notifications. 

The last thing you need to do is update the <script> tag in the src/App.svelte component to match the following:

<script>
  import "izitoast/dist/css/iziToast.min.css";
  import "izitoast/dist/js/iziToast.min";
  import "@zoom/videosdk-ui-toolkit/dist/videosdk-ui-toolkit.css";
  import { connectToRoom, notify } from "./Helper";

  let hasJoinedRoom = false;

  const leaveSessionHandler = () => {
    hasJoinedRoom = false;
  };

  const handleSubmit = async (e) => {
    const formData = new FormData(e.target);
    const roomName = formData.get("roomName");
    const videoContainer = document.getElementById("remote-media");
    try {
      await connectToRoom(roomName, videoContainer, leaveSessionHandler);
      hasJoinedRoom = true;
    } catch (e) {
      notify(e.message, true);
    }
  }
</script>

Here, you made four changes:

  1. Add the CSS import for the Zoom UI toolkit

  2. Add a new function named leaveSessionHandler(), which resets the value of hasJoinedRoom to false — allowing the user to join another session

  3. Add leaveSessionHandler as the third parameter to the connectToRoom() function

  4. In the event of an error when connecting to the room, the notify() function is used to display an error message

And that’s it for the frontend! Run the frontend using the following command.

npm run dev

Test the upgraded application

By default, the application will be running on port 8080. Opening it in your browser of choice, it should work similar to the recording below.

Web interface of Svelte Go Twilio Video Chat App with a field to enter room name and a submit button.

The app is now upgraded

There you have it. You’ve successfully upgraded from Twilio to Zoom Video in your Svelte/Golang application. Because of their similar APIs, you were able to transition seamlessly from Twilio Video to Zoom Video — only having to change the code for JWT generation.

You can review the final codebase for this article on GitHub, should you get stuck at any point. I’m excited to see what else you come up with. Until next time, make peace not war ✌🏾

Joseph Udonsak is a software engineer with a passion for solving challenges — be it building applications, or conquering new frontiers on Candy Crush. When he’s not staring at his screens, he enjoys a cold beer and laughs with his family and friends. Find him at LinkedIn, Medium, and Dev.to.