This documentation is for reference only. We are no longer onboarding new customers to Programmable Video. Existing customers can continue to use the product until December 5, 2026.
We recommend migrating your application to the API provided by our preferred video partner, Zoom. We've prepared this migration guide to assist you in minimizing any service disruption.
This is the first part of a two-part tutorial for creating a video web application with a Node/Express backend and a JavaScript frontend. In this section, you'll set up a backend server that creates free Twilio WebRTC Go video rooms and generates Access Tokens for video room participants. In part two, you'll create the frontend side of the application, where participants can join a video room and share their video and audio with other participants.
At the end of this full tutorial, you'll have a web application that allows you to join a two-person video room and video chat with another person.
If you have already completed the backend section of this tutorial, jump over to Part Two. Otherwise, let's get going!
Open a new terminal window and navigate to the directory where you want your project to live. Then, create a project folder and change into this directory:
_10mkdir video_tutorial && cd video_tutorial
To start, you'll need to collect a few values from the Twilio Console so that you can connect your application to Twilio. You will store these values in a .env
file, and your server will read in these values.
Within the new project folder you created above, create a file called .env
and open it in your preferred text editor.
The first value you'll need is your Account SID, which you can find in the Twilio Console. Once you've gotten that value, store it in the .env
file:
_10TWILIO_ACCOUNT_SID=<your account sid>
Next, you'll need to create an API key. This is what you'll use to authenticate with Twilio when making API calls.
You can create an API key using the Twilio CLI, the REST API, or the Twilio Console. This tutorial will show how to generate it via the Console.
To generate the API Key from the Twilio Console:
When you've created the key, you'll see the friendly name, type, key SID, and API key secret.
Make sure to copy the secret now, because you'll only be able to see it once. When you leave this page, you won't be able to see the secret again.
Copy the API Key ID and the API Key Secret and store both values in the .env
file.
_10TWILIO_ACCOUNT_SID=<your account sid>_10TWILIO_API_KEY_SID=<key sid>_10TWILIO_API_KEY_SECRET=<secret>
If you're using git for version control, make sure these credentials remain secure and out of version control. To do this, create a .gitignore
file at the root of your project directory. In this file, you can list the files and directories that you want git to ignore from being tracked or committed.
Open the new .gitignore
file in your code editor and add the .env
file. While you're here, you can also add node_modules/
, for the dependencies folder you'll install in the next step.
_10.env_10node_modules/
Great! Now that you've stored those credentials and added the .env
to .gitignore
, you can move on to creating the Express server.
First, set up a new Node.js project with a default package.json
file by running the following command:
_10npm init --yes
Once you have your package.json
file, you're ready to install the needed dependencies.
For this project, you will need the following packages:
env
file into your application
Run the following command to install the dependencies:
_10npm install express twilio dotenv node-dev uuid
If you check your package.json
file now, you'll notice that the packages above have been installed as dependencies
.
You will need a server to generate Access Tokens (to grant participants permission to access a video room) and serve the frontend code that you'll build in Part Two of this tutorial. There are several options for creating web servers with Node.js, but this tutorial uses Express.
This section walks through the general setup for a basic Express server. In the next section, you'll add the Twilio-specific code for creating video rooms.
First, create a new file called server.js
at the root of the project directory. This will be the server file where you put all the core logic for your web server. Open that file in your text editor and copy and paste the following code into the file:
_23require("dotenv").config();_23const { v4: uuidv4 } = require("uuid");_23const AccessToken = require("twilio").jwt.AccessToken;_23const VideoGrant = AccessToken.VideoGrant;_23const express = require("express");_23const app = express();_23const port = 5000;_23_23// use the Express JSON middleware_23app.use(express.json());_23_23_23// create the twilioClient_23const twilioClient = require("twilio")(_23 process.env.TWILIO_API_KEY_SID,_23 process.env.TWILIO_API_KEY_SECRET,_23 { accountSid: process.env.TWILIO_ACCOUNT_SID }_23);_23_23// Start the Express server_23app.listen(port, () => {_23 console.log(`Express server running on port ${port}`);_23});
This code pulls in the required dependencies for the server, loads the environment variables from your .env
file, starts a new Express application, and sets the application to run on port 5000. It also creates a Twilio client with the Twilio Node helper library. You'll use this client to communicate with Twilio.
At the bottom of the code, you start the Express server on port 5000.
Now, open package.json
in your code editor. Inside the scripts
section, add a start
script as shown below. You can replace the test
script that was automatically generated by npm init --yes
earlier:
_10 "scripts": {_10 "start": "node-dev server.js"_10 },
To run the start
script, return to your terminal window and run the following command:
_10npm start
Once you have done this, you should see the following log statement in your terminal window, letting you know that the Express server is running:
_10Express server running on port 5000
You'll use the twilioClient
you created earlier in server.js
, and write a function to create new video rooms.
In server.js
, underneath where you created the twilioClient
variable, paste in the following function:
_18const findOrCreateRoom = async (roomName) => {_18 try {_18 // see if the room exists already. If it doesn't, this will throw_18 // error 20404._18 await twilioClient.video.rooms(roomName).fetch();_18 } catch (error) {_18 // the room was not found, so create it_18 if (error.code == 20404) {_18 await twilioClient.video.rooms.create({_18 uniqueName: roomName,_18 type: "go",_18 });_18 } else {_18 // let other errors bubble up_18 throw error;_18 }_18 }_18};
In the code above, you create a function called findOrCreateRoom
, which takes in a room name and checks if an in-progress video room with that name already exists for your account. If that room doesn't exist, you'll get Error 20404, which will indicate that you should create the room.
This function will create the room as a WebRTC Go room (type: "go"
), which is a free room that can have up to two participants.
Eventually, you'll use this function to allow a participant to specify a room to either create or join. In the next section, you'll write a function to create an Access Token for a participant.
Here's the full server.js
code with the new find_or_create_room
function:
_41require("dotenv").config();_41const { v4: uuidv4 } = require("uuid");_41const AccessToken = require("twilio").jwt.AccessToken;_41const VideoGrant = AccessToken.VideoGrant;_41const express = require("express");_41const app = express();_41const port = 5000;_41_41// use the Express JSON middleware_41app.use(express.json());_41_41// create the twilioClient_41const twilioClient = require("twilio")(_41 process.env.TWILIO_API_KEY_SID,_41 process.env.TWILIO_API_KEY_SECRET,_41 { accountSid: process.env.TWILIO_ACCOUNT_SID }_41);_41_41const findOrCreateRoom = async (roomName) => {_41 try {_41 // see if the room exists already. If it doesn't, this will throw_41 // error 20404._41 await twilioClient.video.rooms(roomName).fetch();_41 } catch (error) {_41 // the room was not found, so create it_41 if (error.code == 20404) {_41 await twilioClient.video.rooms.create({_41 uniqueName: roomName,_41 type: "go",_41 });_41 } else {_41 // let other errors bubble up_41 throw error;_41 }_41 }_41};_41_41// Start the Express server_41app.listen(port, () => {_41 console.log(`Express server running on port ${port}`);_41});
Now, you'll create a function that returns an Access Token for a participant. An Access Token gives a participant permission to join video rooms.
The Access Token will be in the JSON Web Token (JWT) standard. The Node Twilio helper library contains functions for creating and decoding these tokens in the JWT format.
Copy and paste the following getAccessToken
function in server.js
, under the findOrCreateRoom
function:
_19const getAccessToken = (roomName) => {_19 // create an access token_19 const token = new AccessToken(_19 process.env.TWILIO_ACCOUNT_SID,_19 process.env.TWILIO_API_KEY_SID,_19 process.env.TWILIO_API_KEY_SECRET,_19 // generate a random unique identity for this participant_19 { identity: uuidv4() }_19 );_19 // create a video grant for this specific room_19 const videoGrant = new VideoGrant({_19 room: roomName,_19 });_19_19 // add the video grant_19 token.addGrant(videoGrant);_19 // serialize the token and return it_19 return token.toJwt();_19};
The function does the following:
Creates an Access Token (in JWT format)
The participant identity doesn't need to be a random string — it could be a value like an email, a user's name, or a user ID. However, it does need to be a unique value for the specific room. You cannot create more than one token for a given participant identity in a room.
The Video Grant is important to add to the token, because it is the piece that allows a participant to connect to video rooms. You can limit the participant's access to a particular video room (which the code above does), or you can generate a token with general access to video rooms.
If you were going to connect this application with other Twilio services, such as Twilio Sync or Twilio Conversations, you could create additional Sync or Conversation grants and add them to this token to allow access to those services as well.
Here's the full server code with the added getAccessToken
function:
_61require("dotenv").config();_61const { v4: uuidv4 } = require("uuid");_61const AccessToken = require("twilio").jwt.AccessToken;_61const VideoGrant = AccessToken.VideoGrant;_61const express = require("express");_61const app = express();_61const port = 5000;_61_61// use the Express JSON middleware_61app.use(express.json());_61_61// create the twilioClient_61const twilioClient = require("twilio")(_61 process.env.TWILIO_API_KEY_SID,_61 process.env.TWILIO_API_KEY_SECRET,_61 { accountSid: process.env.TWILIO_ACCOUNT_SID }_61);_61_61const findOrCreateRoom = async (roomName) => {_61 try {_61 // see if the room exists already. If it doesn't, this will throw_61 // error 20404._61 await twilioClient.video.rooms(roomName).fetch();_61 } catch (error) {_61 // the room was not found, so create it_61 if (error.code == 20404) {_61 await twilioClient.video.rooms.create({_61 uniqueName: roomName,_61 type: "go",_61 });_61 } else {_61 // let other errors bubble up_61 throw error;_61 }_61 }_61};_61_61const getAccessToken = (roomName) => {_61 // create an access token_61 const token = new AccessToken(_61 process.env.TWILIO_ACCOUNT_SID,_61 process.env.TWILIO_API_KEY_SID,_61 process.env.TWILIO_API_KEY_SECRET,_61 // generate a random unique identity for this participant_61 { identity: uuidv4() }_61 );_61 // create a video grant for this specific room_61 const videoGrant = new VideoGrant({_61 room: roomName,_61 });_61_61 // add the video grant_61 token.addGrant(videoGrant);_61 // serialize the token and return it_61 return token.toJwt();_61};_61_61// Start the Express server_61app.listen(port, () => {_61 console.log(`Express server running on port ${port}`);_61});
Next, you'll create a route called /join-room
. In Part Two of this Tutorial, your frontend application will make a POST request to this /join-room
route with a roomName
in the body of the request.
Copy and paste the following code in server.js
, underneath the route that returns "In progress!":
_14app.post("/join-room", async (req, res) => {_14 // return 400 if the request has an empty body or no roomName_14 if (!req.body || !req.body.roomName) {_14 return res.status(400).send("Must include roomName argument.");_14 }_14 const roomName = req.body.roomName;_14 // find or create a room with the given roomName_14 findOrCreateRoom(roomName);_14 // generate an Access Token for a participant in this room_14 const token = getAccessToken(roomName);_14 res.send({_14 token: token,_14 });_14});
This route takes a POST
request containing a JSON object with a room name, and then calls the find_or_create_room
function and the get_access_token
function. It returns the decoded Access Token, which is a JSON Web Token (JWT).
Here's the final server file with all of these pieces:
_76require("dotenv").config();_76const { v4: uuidv4 } = require("uuid");_76const AccessToken = require("twilio").jwt.AccessToken;_76const VideoGrant = AccessToken.VideoGrant;_76const express = require("express");_76const app = express();_76const port = 5000;_76_76// use the Express JSON middleware_76app.use(express.json());_76_76// create the twilioClient_76const twilioClient = require("twilio")(_76 process.env.TWILIO_API_KEY_SID,_76 process.env.TWILIO_API_KEY_SECRET,_76 { accountSid: process.env.TWILIO_ACCOUNT_SID }_76);_76_76const findOrCreateRoom = async (roomName) => {_76 try {_76 // see if the room exists already. If it doesn't, this will throw_76 // error 20404._76 await twilioClient.video.rooms(roomName).fetch();_76 } catch (error) {_76 // the room was not found, so create it_76 if (error.code == 20404) {_76 await twilioClient.video.rooms.create({_76 uniqueName: roomName,_76 type: "go",_76 });_76 } else {_76 // let other errors bubble up_76 throw error;_76 }_76 }_76};_76_76const getAccessToken = (roomName) => {_76 // create an access token_76 const token = new AccessToken(_76 process.env.TWILIO_ACCOUNT_SID,_76 process.env.TWILIO_API_KEY_SID,_76 process.env.TWILIO_API_KEY_SECRET,_76 // generate a random unique identity for this participant_76 { identity: uuidv4() }_76 );_76 // create a video grant for this specific room_76 const videoGrant = new VideoGrant({_76 room: roomName,_76 });_76_76 // add the video grant_76 token.addGrant(videoGrant);_76 // serialize the token and return it_76 return token.toJwt();_76};_76_76app.post("/join-room", async (req, res) => {_76 // return 400 if the request has an empty body or no roomName_76 if (!req.body || !req.body.roomName) {_76 return res.status(400).send("Must include roomName argument.");_76 }_76 const roomName = req.body.roomName;_76 // find or create a room with the given roomName_76 findOrCreateRoom(roomName);_76 // generate an Access Token for a participant in this room_76 const token = getAccessToken(roomName);_76 res.send({_76 token: token,_76 });_76});_76_76// Start the Express server_76app.listen(port, () => {_76 console.log(`Express server running on port ${port}`);_76});
Test this new route by running the server (with the command npm start
) and making a POST request to http://localhost:5000/join-room
. You can use curl, Postman, HTTPie, or another tool for making this request. To make the request using curl
, run the following command in your terminal:
_10curl -X POST http://localhost:5000/join-room \_10-H "Content-Type: application/json" \_10--data '{"roomName": "test room!"}'
You should receive output similar to the output below:
_10{_10 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImN0..."_10}
You can use the site jwt.io to inspect the token you received and see the different components that make up the Access Token. If you paste the token you received into the jwt.io debugger, it will decode the token and show you what the token includes. You should see that it contains a video grant for the specific room you created. The token will also include fields with other information you provided:
iss
: your
TWILIO_API_KEY_SID
sub
: your
TWILIO_ACCOUNT_SID
identity
: the randomly generated uuid for the participant's identity
You now have a working backend server that will create video rooms and generate Access Tokens! You're done with this section of the tutorial and can move on to Part Two, where you'll create the frontend for this web app.