How to use TURN server with PeerJs
Tags: webdev, javascript, webrtc, tutorial
Published: 2024-04-05 20:32:48

In this article we are going to learn how we can use the TURN server with the PeerJS

For this, we are going to create a new PeerJs project

This is going to be a simple webrtc application for learning purposes.

The App is going to connect to another peer using PeerJs and send messages, we are going to use TURN server to bypass the NAT in this example.

For a TURN server we are going to use the Metered TURN servers 

Here is what we are going to learn in this article

- Getting a TURN server solution (Metered TURN servers)

- Setting Up a Simple WebRTC Project Using PeerJS
- Integrating Metered TURN servers with PeerJs project
- Testing the TURN server and the app

In order to integrate TURN server with PeerJs application, you first need a TURN server. 

In order to make things easier, we are going to use the Metered TURN service in this example


![Metered TURN Servers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zcfkvub6z0vdshcsi551.png)

## Getting a TURN server Solution [Metered TURN servers](https://metered.ca/stun-turn)

### Step 1: Create a Free account

go to the Metered TURN server website: [https://metered.ca/stun-turn](https://metered.ca/stun-turn)

There create a free account. You get unlimited free STUN server usage and 5 gb of free turn server usage when you put in a credit card. That should be enough for our example here

you can also purchase a plan if you like the paid plans are as follows

- **TURN Growth 150 GB :** 99 USD /mo, free 150 GB TURN usage included, 0.40 USD/ GB overage, **99.95% Uptime**
- **TURN Business 500 GB:** 199 USD /mo, free 500 GB TURN usage included, 0.20 USD/ GB overage, **99.99% Uptime**
- **TURN Enterprise 2 TB:** 499 USD /mo, free 2 TB TURN usage included, 0.10 USD/ GB overage, **99.999% Uptime**

### Step 2: Creating TURN credentials

When you signup you land up in the dashboard there you can create the TURN credentials

1. You can manually create credentials and use it in your PeerJS library or

2. You can use the API to create the credentials as well

While creating the credentials manually is straightforward using the API gives you a lot more features and management control over the TURN server like for example


- Add/Remove credentials using the API 
- Fetch Per-Credential Usage Metrics with API
- Enable/Disable teh Credentials with the API
- Create auto expiring TURN credentials

You can also select the region, where your TURN server should be located. The recommended is the** Global location**, which automatically routes the traffic to the turn server that is nearest to your users location

apart from this the locations available are

- Global
- North America Only
- E.U Only
- Europe Only
- Asia Only 
- Oceana Only
- U.S West Only
- U.S Central Only
- U.S East Only
- Canada Central Only
- Canada East Only
- Europe West Only
- Europe Central Only
- Asia West Only
- Asia East Only
- Oceana Only
- Canada Only
- U.S Only
- U.K Only
- Seoul Only
- Singapore Only
- India Only
- Australia Only


![Metered Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0k240xzs3t8j77q1mbg7.png)

You can click on the instructions button to know more about the how to use the credential in your app


![Peer Js project with Metered TURN Servers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lavnin1pjpmcrejq87e.png)

## Setting Up a Simple WebRTC Project Using PeerJS

PeerJS is a wrapper around the web browsers built in webRTC implementation that provides a easy to use peer to peer connection API.

This API is complete, configurable and easy to use API, that handles serialization, peer discovery and connection management

PeerJs is easy to setup for audio/video streaming, file sharing and data channels

### Creating a PeerJs Project
In this section we are going to create a new PeerJs project

As stated above, the app is going to be simple webrtc app that is going to connec to another peer and send messages using a TURN server

For a TURN server we are going to use the Metered TURN servers. We have already stated how you can Sign Up for the Metered TURN service above

First, create a nodejs project, create a new directory and name it `peerjs` then `cd` into it 

**step 1:** type the npm init command to initialize a new project. This will create a `package.json` file which will look something like this

```json
{
  "name": "peerjs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "metered-turn-servers",
  "license": "ISC"
}
```

## Creating the UI of the App. (index.html)

In the root folder create a new file and name it `index.html`

In the file paste the following code:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PeerJS Example with TURN Server</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.3.2/peerjs.min.js"></script>
    <script src="app.js" defer></script>
</head>
<body>
    <h2>PeerJS WebRTC Communication</h2>
    <div>
        <label for="peerId">Your Peer ID:</label>
        <input type="text" id="peerId" readonly>
    </div>
    <div>
        <label for="otherPeerId">Connect to Peer ID:</label>
        <input type="text" id="otherPeerId">
        <button id="connectButton">Connect</button>
    </div>
    <div>
        <label for="message">Message:</label>
        <input type="text" id="message">
        <button id="sendMessageButton">Send Message</button>
    </div>
    <div id="messages"></div>
</body>
</html>
```
What are we doing here:
1. We are importing the peerjs library through CDN. This enables us to use the library

2. Then we are linking to the external JavaScript File called the `app.js`, this file contains the logic of the PeerJs app that we are creating

3. **Peer ID Display:** In the body section we are creating a label and input that shows the user their own PeerJS ID like so

```html
<div>
    <label for="peerId">Your Peer ID:</label>
    <input type="text" id="peerId" readonly>
</div>
```

4 **Connect to Peer** Then in the next section we are allowing the user to connect to another user by putting in the credentials, that is the PeerJs ID of the other user that they want to connect to 

```html
<div>
    <label for="otherPeerId">Connect to Peer ID:</label>
    <input type="text" id="otherPeerId">
    <button id="connectButton">Connect</button>
</div>
```
5 **Send Message:** Here we have an input field where the user can write a message that they want to send to another user

```html
<div>
    <label for="message">Message:</label>
    <input type="text" id="message">
    <button id="sendMessageButton">Send Message</button>
</div>
```

6 **Message Display**: It is a `<div>` with nothing inside adn an id of `messages`. In this div we are going to add the messages that are send and received

```html
<div id="messages"></div>
```


## Creating the APP logic (app.js file)

Next create a file in the root directory and name it `app.js`

Paste the following code in the `app.js` file

```js
document.addEventListener('DOMContentLoaded', () => {
    const peer = new Peer(undefined, {
        config: {
            iceServers: [
                {
                    urls: "turn:global.relay.metered.ca:80", // Replace with your TURN server URL
                    username: "370e0c14a753092e97cd645f", // Replace with your TURN username
                    credential: "TJ+gyUoqt2xo3oI2", // Replace with your TURN credential
               }
            ]
        }
    });

    const peerIdInput = document.getElementById('peerId');
    const otherPeerIdInput = document.getElementById('otherPeerId');
    const connectButton = document.getElementById('connectButton');
    const messageInput = document.getElementById('message');
    const sendMessageButton = document.getElementById('sendMessageButton');
    const messagesDiv = document.getElementById('messages');

    let dataConnection = null;

    peer.on('open', id => {
        peerIdInput.value = id;
    });

    connectButton.addEventListener('click', () => {
        const otherPeerId = otherPeerIdInput.value;
        if (!otherPeerId) {
            alert('Please enter the other peer ID.');
            return;
        }

        dataConnection = peer.connect(otherPeerId);
        setupDataConnectionEvents();
    });

    sendMessageButton.addEventListener('click', () => {
        const message = messageInput.value;
        if (!message) {
            alert('Please enter a message to send.');
            return;
        }

        if (dataConnection && dataConnection.open) {
            dataConnection.send(message);
            displayMessage(`You: ${message}`);
            messageInput.value = ''; // Clear the input after sending
        } else {
            alert('Data connection is not established. Please connect to a peer first.');
        }
    });

    peer.on('connection', connection => {
        dataConnection = connection;
        setupDataConnectionEvents();
    });

    function setupDataConnectionEvents() {
        dataConnection.on('data', message => {
            displayMessage(`Peer: ${message}`);
        });

        dataConnection.on('open', () => {
            messagesDiv.innerHTML += '<p>Connection established. You can send messages now.</p>';
        });

        dataConnection.on('close', () => {
            alert('Data connection has been closed.');
        });
    }

    function displayMessage(message) {
        messagesDiv.innerHTML += `<p>${message}</p>`;
    }
});
```
What are we doing here:

## (Important) Initializing the code and adding Global variables
let us look at the initializing code and understand what it is doing

```js
document.addEventListener('DOMContentLoaded', () => {
    const peer = new Peer(undefined, {
        config: {
            iceServers: [
                {
                    urls: "turn:global.relay.metered.ca:80", // Use the TURN server URL from Metered
                    username: "370e0c14a753092e97cd645f", // TURN server username Obtained from metered.ca/stun-turn website
                    credential: "TJ+gyUoqt2xo3oI2", // TURN server credentials Obtained from metered.ca/stun-turn
               }
            ]
        }
    });
```
* We wait for the DON to load then we call a function
* this function creates a new PeerJs `Peer` Object. and we pass the **undefined** as the first argument here
* In the config object we add the Metered TURN credentials that we obtained from the metered turn server website: https://metered.ca/stun-turn

### UI Elements and Connection Handling

```js
const peerIdInput = document.getElementById('peerId');
const otherPeerIdInput = document.getElementById('otherPeerId');
const connectButton = document.getElementById('connectButton');
const messageInput = document.getElementById('message');
const sendMessageButton = document.getElementById('sendMessageButton');
const messagesDiv = document.getElementById('messages');

let dataConnection = null;
```
* You get the elements from the index.html page like peer ID, input messages etc
* `dataConnection` is initialized with null

### Displaying the Peer ID

```js
peer.on('open', id => {
    peerIdInput.value = id;
});
```

Here the peer js listner waits for the `open` event. when the peer ID is ready, the id is displayed in the `peerIdInput` field.

### Establishing a Connection

```js
connectButton.addEventListener('click', () => {
    const otherPeerId = otherPeerIdInput.value;
    if (!otherPeerId) {
        alert('Please enter the other peer ID.');
        return;
    }

    dataConnection = peer.connect(otherPeerId);
    setupDataConnectionEvents();
});
```
* When the user clicks on the **Connect** button, the script to establish a connection to the perr whose ID is entered in the `otherPeerInput` field runs
* It calles the `peer.connect` and passes the other peer's ID and assigns the returned data connection object to `dataConnection`
`setupDataConnectionEvents` is called to initiaze event listners for the new connection

### Sending Messages

```js
sendMessageButton.addEventListener('click', () => {
    const message = messageInput.value;
    if (!message) {
        alert('Please enter a message to send.');
        return;
    }

    if (dataConnection && dataConnection.open) {
        dataConnection.send(message);
        displayMessage(`You: ${message}`);
        messageInput.value = '';
    } else {
        alert('Data connection is not established. Please connect to a peer first.');
    }
});
```
* Here we are listning on the `Send message` button. If something is typed in the input field and the connection is established we send the message
* The sent messages are also displayed on the `messagesDiv` after the `You:` text and the input is cleared

### Receiving Connections and Messages

```js
peer.on('connection', connection => {
    dataConnection = connection;
    setupDataConnectionEvents();
});
```
Nest we are listning for the connections when another peer connects to this peer then we save the connection object in the `dataConnection` variable and similar to outgoing connections the event listners are setup by the library

### Handling connection events

```js
function setupDataConnectionEvents() {
    dataConnection.on('data', message => {
        displayMessage(`Peer: ${message}`);
    });

    dataConnection.on('open', () => {
        messagesDiv.innerHTML += '<p>Connection established. You can send messages now.</p>';
    });

    dataConnection.on('close', () => {
        alert('Data connection has been closed.');
    });
}
```

Here there are a few things that are hapening

* `setupDataConnectionEvents` function adds listners for the data, open and close events to the current data connection
* `data` event listener displays incoming messages
* `open` event adds a message to the `messageDiv`, telling us that a connection has been established
`close` event alerts the user that the connection has been closed

### Displaying Messages

```js
function displayMessage(message) {
    messagesDiv.innerHTML += `<p>${message}</p>`;
}
```

this function adds messages to the `messagesDiv` thus showing messages on the screen



![Metered TURN Server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yb5vwarvdev16sr5ur3b.png)

## [Metered TURN servers](https://metered.ca/stun-turn)

1. **API:** TURN server management with powerful API. You can do things like Add/ Remove credentials via the API, Retrieve Per User / Credentials and User metrics via the API, Enable/ Disable credentials via the API, Retrive Usage data by date via the API.
2. **Global Geo-Location targeting:** Automatically directs traffic to the nearest servers, for lowest possible latency and highest quality performance. less than 50 ms latency anywhere around the world
3. **Servers in 12 Regions of the world:** Toronto, Miami, San Francisco, Amsterdam, London, Frankfurt, Bangalore, Singapore,Sydney, Seoul
4. **Low Latency:** less than 50 ms latency, anywhere across the world.
5. **Cost-Effective:** pay-as-you-go pricing with bandwidth and volume discounts available.
6. **Easy Administration:** Get usage logs, emails when accounts reach threshold limits, billing records and email and phone support.
7. **Standards Compliant:** Conforms to RFCs 5389, 5769, 5780, 5766, 6062, 6156, 5245, 5768, 6336, 6544, 5928 over UDP, TCP, TLS, and DTLS.
8. **Multi‑Tenancy:** Create multiple credentials and separate the usage by customer, or different apps. Get Usage logs, billing records and threshold alerts.
9. **Enterprise Reliability:** 99.999% Uptime with SLA.
10. Enterprise Scale: With no limit on concurrent traffic or total traffic. Metered TURN Servers provide Enterprise Scalability
11. **5 GB/mo Free:** Get 5 GB every month free TURN server usage with the Free Plan
12. Runs on port 80 and 443
13. Support TURNS + SSL to allow connections through deep packet inspection firewalls.
14. Support STUN
15. Supports both TCP and UDP

## Conclusion
Thus in this article we have learned how we can add a turn server to a peer js project.