Documentation

Build a chat app in the NexoRouter documentation.

Build a chat app

This tutorial builds a multi-turn terminal chat with Node.js, the OpenAI SDK, and NexoRouter's stable Chat Completions API.

The example uses non-streaming requests to stay within the verified public surface.

What you will build

  • A client configured with the NexoRouter base URL.
  • Conversation history preserved across turns.
  • Basic error handling.
  • Usage output for checking token counts.
  • A flow that is easy to move into a backend API.

Requirements

  • Node.js 20 or newer.
  • An account with prepaid balance.
  • An active API key.
  • A current model ID copied from Models.

1. Create the project

mkdir nexorouter-chat
cd nexorouter-chat
npm init -y
npm pkg set type=module
npm install openai dotenv

Create .gitignore:

node_modules
.env

Create .env:

NEXOROUTER_API_KEY=your_nexorouter_key
NEXOROUTER_MODEL=deepseek-v4-flash

Never commit .env.

2. Send the first message

Create first-message.mjs:

import "dotenv/config";
import OpenAI from "openai";

if (!process.env.NEXOROUTER_API_KEY) {
  throw new Error("Missing NEXOROUTER_API_KEY");
}

const client = new OpenAI({
  apiKey: process.env.NEXOROUTER_API_KEY,
  baseURL: "https://api.nexorouter.com/v1",
  timeout: 60_000,
  maxRetries: 2,
});

const response = await client.chat.completions.create({
  model: process.env.NEXOROUTER_MODEL || "deepseek-v4-flash",
  messages: [
    { role: "system", content: "You are a concise technical assistant." },
    { role: "user", content: "Give me a three-step API launch checklist." },
  ],
  max_tokens: 256,
});

console.log(response.choices[0]?.message?.content);
console.log("usage:", response.usage);

Run it:

node first-message.mjs

Then open Usage Logs and confirm model ID, status, tokens, cost, latency, and request ID.

3. Turn the script into multi-turn chat

Create chat.mjs:

import "dotenv/config";
import OpenAI from "openai";
import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";

if (!process.env.NEXOROUTER_API_KEY) {
  throw new Error("Missing NEXOROUTER_API_KEY");
}

const client = new OpenAI({
  apiKey: process.env.NEXOROUTER_API_KEY,
  baseURL: "https://api.nexorouter.com/v1",
  timeout: 60_000,
  maxRetries: 2,
});

const model = process.env.NEXOROUTER_MODEL || "deepseek-v4-flash";
const messages = [
  {
    role: "system",
    content: "You are a concise assistant. State uncertainty and never invent API behavior.",
  },
];
const terminal = createInterface({ input, output });

console.log(`Connected to ${model}. Type /exit to stop.`);

while (true) {
  const text = (await terminal.question("you> ")).trim();
  if (!text) continue;
  if (text === "/exit") break;

  messages.push({ role: "user", content: text });

  try {
    const response = await client.chat.completions.create({
      model,
      messages,
      max_tokens: 500,
    });

    const answer = response.choices[0]?.message?.content ?? "";
    messages.push({ role: "assistant", content: answer });
    console.log(`assistant> ${answer}`);
    console.log(`tokens> ${response.usage?.total_tokens ?? "not reported"}`);
  } catch (error) {
    console.error("request failed>", error?.status, error?.error?.code, error?.message);
  }
}

terminal.close();

Run it:

node chat.mjs

4. Change models

Do not depend forever on the tutorial's model ID. Copy an available model from Models or query:

curl https://api.nexorouter.com/v1/models \
  -H "Authorization: Bearer $NEXOROUTER_API_KEY"

Then change:

NEXOROUTER_MODEL=exact-model-id

Model IDs are case-sensitive. Also check the model page for price, billing unit, and endpoint.

5. Take it to production

Before exposing it to users:

  1. Move the call to a backend; never publish the API key.
  2. Create a dedicated key with a budget and model scope.
  3. Limit message length, max_tokens, and concurrency.
  4. Keep only the history needed to control cost and privacy.
  5. Add authentication, per-user rate limits, and input validation.
  6. Respect retry-after and bound retries.
  7. Log request ID, model, and status without secrets.
  8. Test key, balance, model, timeout, and rate-limit failures.

Common problems

SymptomCheck
401 invalid_api_keyCorrect key, Bearer header, active key, and base URL.
404 model_not_foundExact ID, availability, and the key's model scope.
403 insufficient_quotaWorkspace balance, key budget, and key scope.
429 rate_limit_exceededretry-after, concurrency, and prompt size.
413 request_too_largeReduce history or split the input.
502 or 504Bounded retry, client timeout, or another model.

Next

Build a chat app — NexoRouter