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:
- Move the call to a backend; never publish the API key.
- Create a dedicated key with a budget and model scope.
- Limit message length,
max_tokens, and concurrency. - Keep only the history needed to control cost and privacy.
- Add authentication, per-user rate limits, and input validation.
- Respect
retry-afterand bound retries. - Log request ID, model, and status without secrets.
- Test key, balance, model, timeout, and rate-limit failures.
Common problems
| Symptom | Check |
|---|---|
401 invalid_api_key | Correct key, Bearer header, active key, and base URL. |
404 model_not_found | Exact ID, availability, and the key's model scope. |
403 insufficient_quota | Workspace balance, key budget, and key scope. |
429 rate_limit_exceeded | retry-after, concurrency, and prompt size. |
413 request_too_large | Reduce history or split the input. |
502 or 504 | Bounded retry, client timeout, or another model. |