# JavaScript / Node.js

`turnix-js` is a small, dependency-free wrapper around [`POST /credentials/ice`](https://turnix.io/docs/api-reference/credentials). It is meant for **server-side** JavaScript: Node.js 18 or newer, Deno, Bun, or edge runtimes with `fetch`.

Package: [turnix-js on npm](https://www.npmjs.com/package/turnix-js) · Source: [github.com/turnixio/turnix-js](https://github.com/turnixio/turnix-js)

> **Warning: 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.

**AI prompt: Integrate Turnix with turnix-js**

```text
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](https://turnix.io/docs/api-reference/credentials#parameters):

| Option | Sent as | Description |
|--------|---------|-------------|
| `ttl` | `ttl` | Lifetime in seconds (whole number). |
| `room` | `room` | [Room label](https://turnix.io/docs/concepts/clients-rooms). |
| `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](https://turnix.io/docs/api-reference/credentials) 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](https://github.com/turnixio/turnix-js-demo) is a minimal Express + WebSocket signaling app that uses the SDK for a two-party video call.
