SDKs / JavaScript / Node.js

JavaScript / Node.js

turnix-js is a small, dependency-free wrapper around POST /credentials/ice. It is meant for server-side JavaScript: Node.js 18 or newer, Deno, Bun, or edge runtimes with fetch.

Package: turnix-js on npm · Source: github.com/turnixio/turnix-js

Server-side only

The SDK needs your API token. Do not bundle it into browser code. Call it from your backend and send only the resulting iceServers to the client.

Integrate Turnix with turnix-js
Add Turnix (managed TURN/STUN servers) to the WebRTC app in this repository, using the turnix-js SDK on the Node.js backend.

Context about turnix-js and Turnix:
- Install: npm install turnix-js (ES module, Node.js 18+): import { TurnixIO } from "turnix-js".
- const turnix = new TurnixIO({ bearerToken: process.env.TURNIX_API_TOKEN }); const iceServers = await turnix.requestCredentials({ ttl, room, initiatorClient, receiverClient, preferredRegion, fixedRegion }) resolves to the iceServers array; on non-2xx it rejects with an Error containing the status and body.
- The token is a secret project API token. Use it only on the server, never send it to the browser, never log or commit it.
- room, initiatorClient and receiverClient may contain only letters, digits, "-" and "_".
- Docs: https://turnix.io/docs/sdks/javascript and https://turnix.io/docs/api-reference/credentials

Do the following:
1. Find the backend (Express, Fastify, Next.js route handlers, …) and where the browser creates RTCPeerConnection. Follow the existing conventions. If the backend is not JavaScript, stop and tell me to use the generic prompt at https://turnix.io/docs/getting-started/ai-agents instead.
2. Install turnix-js and create one TurnixIO instance at startup. Fail at startup with a clear message if TURNIX_API_TOKEN is missing.
3. Add an authenticated endpoint (for example GET /api/turn-credentials) that calls requestCredentials({ ttl: 3600, room, initiatorClient }) with sanitized call and user IDs, and returns { iceServers }. On errors, log the message without the token and return 502 (503 with "TURN usage limit reached" when the message contains 429).
4. In the browser, fetch the endpoint before creating each RTCPeerConnection and pass { iceServers }. Do not cache credentials in localStorage.
5. Add TURNIX_API_TOKEN to .env.example and the README, and a test that mocks TurnixIO and checks that the endpoint returns iceServers and requires login.
6. Summarize the changes and what I still need to do (setting TURNIX_API_TOKEN in production).

Install#

bash
npm install turnix-js

The package is an ES module:

javascript
import { TurnixIO } from "turnix-js";

Request credentials#

javascript
import express from "express";
import { TurnixIO } from "turnix-js";

const turnix = new TurnixIO({ bearerToken: process.env.TURNIX_API_TOKEN });
const app = express();

app.get("/api/turn-credentials", requireLogin, async (req, res) => {
  try {
    const iceServers = await turnix.requestCredentials({
      ttl: 3600,
      room: "room-42",
      initiatorClient: `user-${req.user.id}`,
    });
    res.json({ iceServers });
  } catch (err) {
    console.error(err.message); // e.g. "Failed to fetch ICE credentials: 429 {...}"
    res.status(502).json({ error: "Could not get TURN credentials" });
  }
});

In the browser:

javascript
const { iceServers } = await fetch("/api/turn-credentials").then((r) => r.json());
const pc = new RTCPeerConnection({ iceServers });

API#

new TurnixIO(options)#

Option Type Default Description
bearerToken string required Your project API token. Throws Error("Bearer token is required") if missing.
apiUrl string https://turnix.io/api/v1/credentials/ice Full URL of the credentials endpoint.

turnix.requestCredentials(options?)#

Returns a Promise that resolves to the iceServers array. All options are optional and map to the API parameters:

Option Sent as Description
ttl ttl Lifetime in seconds (whole number).
room room Room label.
initiatorClient initiator_client Label of the participant starting the connection.
receiverClient receiver_client Label of the answering participant.
preferredRegion preferred_region Region slug to prefer; falls back to GeoIP if unavailable.
fixedRegion fixed_region Region slug to require; the call fails if unavailable.

On a non-2xx response the promise rejects with an Error whose message contains the HTTP status and the response body, for example Failed to fetch ICE credentials: 422 {"error":"Region Offline"}.

Limitations#

The current version (0.1.4) has a few gaps. If you need any of these, call the HTTP API directly; it is a single fetch call.

  • No expiresAt. Only iceServers is returned. Track expiry yourself (Date.now() + ttl * 1000).
  • No client IP forwarding. The SDK cannot set X-TURN-CLIENT-IP, so the region is chosen from your server's location unless you pass preferredRegion or fixedRegion. The README mentions a clientIp option, but it is not implemented in 0.1.4.
  • ttl: 0 is not sent (falsy values are skipped), so the platform default applies.

Equivalent direct call with client IP forwarding:

javascript
const res = await fetch("https://turnix.io/api/v1/credentials/ice", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TURNIX_API_TOKEN}`,
    "Content-Type": "application/json",
    "X-TURN-CLIENT-IP": req.ip,
  },
  body: JSON.stringify({ ttl: 3600 }),
});
if (!res.ok) throw new Error(`Turnix ${res.status}: ${await res.text()}`);
const { iceServers, expiresAt } = await res.json();

Demo#

turnix-js-demo is a minimal Express + WebSocket signaling app that uses the SDK for a two-party video call.

Last updated October 30, 2026