Video Chat App with Gatsby+Twilio

Video Chat App with Gatsby+Twilio

ยท

8 min read

Hi, everyone ๐Ÿ˜ƒ. I'm Hulya. I have completed my hackathon project, it's a video app that you can speak with your friends. Actually, I had a different project in mind, it was a full-stack NextJS e-commerce app, but I had issues when I try to deploy with Vercel. My app started to break. I have actually started learning NextJS through this hackathon. However, deployment didn't go as I wanted; but I didn't want to give up and thought about a Gatsby project, and I chose to do a video chat app.

Screen Shot 2021-02-07 at 5.05.25 PM.png

You can try the app with remote users or from different browsers.

Inspiration

With the increase of working from home culture, video chat apps became an indispensable part of our lives, especially since Covid-19. People need collaboration tools more than ever. Having a group video chat application that you can customize the โ€œlook and feelโ€ of can be very important since you are not restricted to the features of most commercial providers.

Also, it is a great way to keep in touch with your friends, family, and colleagues and it's so easy to use. I think every age group can use this minimalistic web app.

How I Built It

I have used React hooks like useState, useEffect, and useRef.

Features

  • Our video app asks the user's display name, and we get JWT token and authenticate our user. Users' friends also can log in to our app through the vercel link.

How I Built It

Twilio Setup

Our group chat app handles real-time interactions with the use of serverless Twilio functions. According to docs, Twilio Functions is a serverless environment that empowers developers to quickly and easily create production-grade, event-driven Twilio applications that scale with their businesses. By setting up a Twilio account we can support real-time video calling.

Signing up for Twilio is a very easy process. We can start using Twilio for free.

Screen Shot 2021-02-07 at 12.38.15 PM.png

After creating the account, I have used Programmable Video service that can be found from the menu and saved my Account SID, and AUTH TOKEN, API Keys, and API Secret in a .env file. You can find those under the Tools and API Keys option from the menu.

Screen Shot 2021-02-07 at 12.42.21 PM.png

After saving these 4 environment variables, it is time to create our Twilio Functions.

Screen Shot 2021-02-07 at 12.45.58 PM.png

I have chosen the classic option to create the functions. We are using built-in Twilio variables and get a new access token from our stored variables, also doing a callback with our JWT token.

Gatsby Setup

After creating my default Gatsby starter project, changed some of the settings and removed the default code. After that, created a login form component for the user with some CSS styling.

Connect Gatsby to Twilio

I have used axios to make an API call to the Twilio endpoint which contains our function.

//  components/loginForm.js
// ....
 const handleSubmit = async event => {
    event.preventDefault()
    const result = await axios({
      method: "POST",
      url: "https://bubbles-peacock-4530.twil.io/create-token",
      data: {
        identity: name,
      },
    })
    console.log(result)
    const jwt = result.data
    storeToken(jwt)
    storeName(name)
  }
// .....

Screen Shot 2021-02-07 at 1.18.23 PM.png

After getting our JWT token, the user is logged in to the app.

Connect to a Room and Display a Camera Preview

Created the video component with the help of twilio-video package. I have made a call for twilio-video with the use of useEffect hook.

After making a connection to our room, it's time to show our local webcam video. To attach the video to the div, I have used useRef hook. There is a built-in createLocalVideoTrack() variable from Twilio to attach the video of the webcam to the div localVideoRef.

Attach Remote Participants and Handle Connected Participants

Added another reference for our remote participants, with useRef hook and attached it to a `div.

// components/video.js

import React, { useEffect, useRef } from "react"
import TwilioVideo from "twilio-video"
import "./video.css"

const Video = ({ token, name }) => {
  const localVideoRef = useRef()
  const remoteVideoRef = useRef()

  useEffect(() => {
    TwilioVideo.connect(token, { video: true, audio: true, name: "SVA" }).then(
      result => {
        TwilioVideo.createLocalVideoTrack().then(track => {
          localVideoRef.current.appendChild(track.attach())
        })

        const addParticipant = participant => {
          participant.tracks.forEach(publication => {
            if (publication.isSubscribed) {
              const track = publication.track
              remoteVideoRef.current.appendChild(track.attach())
            }
          })
          participant.on("trackSubscribed", track => {
            remoteVideoRef.current.appendChild(track.attach())
          })
        }
        result.participants.forEach(addParticipant)
        result.on("participantConnected", addParticipant)
      }
    )
  }, [token])

  return (
    <>
      <h2>Organizer</h2>
      <div ref={localVideoRef} />
      <h2>Remote Participants</h2>
      <div ref={remoteVideoRef} className="remoteVideo" />
      <p>Connected: {name}</p>
    </>
  )
}
export default Video

Challenges I ran into

Making a request to get the token, connecting to the Video service, and manipulating the DOM to connect div elements was a little hard. I was not using useRef so much, with this project I had to remember it and it was pretty easy.

For the JWT authentication, I had a CORS error, later I learned that I needed to add that inside Twilio function with the header rules, like this.

exports.handler = function(context, event, callback) {
    let accessToken = Twilio.jwt.AccessToken;
    let videoGrant = accessToken.VideoGrant;
    let token = new accessToken(process.env.ACCOUNT_SID, process.env.API_KEY, process.env.API_SECRET);
    token.identity = event.identity;
    const grant = new videoGrant();
    token.addGrant(grant)
    let response = new Twilio.Response();
    // Build list of headers
    let headers = {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET,PUT,POST",
    "Access-Control-Allow-Headers": "Content-Type"
};
    // Set headers in response
    response.setHeaders(headers);
    response.setBody(JSON.stringify(token.toJwt()));
    callback(null, response);
};

Accomplishments that I'm proud of

I'm really amazed that building a video chat app, it's not so hard thanks to Twilio API and documentation was very helpful.

Wrapping Up

As part of this project, I have learned how to use Twilio API and serverless functions and made a really useful web app that everyone can use.

If you have any questions about the app, you can reach out to me through Twitter.

Thanks for your time. Like this post? Consider buying me a coffee to support me writing more.

Resources