Tutorials

Building a Chatbot in Node.js

PNPriya NairApr 27, 20264 min read

In this tutorial you will build a small but complete chatbot in Node.js that remembers the conversation, runs in the terminal, and talks to any model through Model Database. We will use the official OpenAI SDK pointed at Model Database, so the same code works across Claude, GPT, Gemini, Llama, and more.

By the end you will have a looping REPL-style chat that maintains history and prints how much each turn costs.

Step 1: Project setup

Create a project and install the SDK. We use ES modules, so set "type": "module" in package.json:

mkdir mdb-chatbot && cd mdb-chatbot
npm init -y
npm pkg set type=module
npm install openai

Export your key so it is not hardcoded:

export MDB_API_KEY="mdb_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Step 2: Configure the client

Create bot.js and point the OpenAI client at Model Database:

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://modeldatabase.com/v1",
  apiKey: process.env.MDB_API_KEY,
});

Step 3: Track conversation history

The chat endpoint is stateless, so the chatbot must keep its own message array and resend it each turn. Start with a system prompt that defines the bot's personality:

const messages = [
  { role: "system", content: "You are a friendly, concise assistant for developers." },
];

Step 4: Build the chat loop

We use Node's built-in readline module to read input from the terminal. Each time the user types, we append their message, call the model, append the reply, and print it:

import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";

const rl = readline.createInterface({ input, output });

while (true) {
  const userText = await rl.question("you > ");
  if (userText.trim() === "/quit") break;

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

  const resp = await client.chat.completions.create({
    model: "anthropic/claude-sonnet-4-6",
    messages,
  });

  const reply = resp.choices[0].message.content;
  messages.push({ role: "assistant", content: reply });
  console.log("bot > " + reply + "\n");
}

rl.close();

Run it with node bot.js. Type messages, and the bot replies with full memory of the conversation because the entire messages array is sent every time. Type /quit to exit.

Step 5: Show the cost of each turn

Model Database returns X-MDB-Charged-USD and X-MDB-Balance-USD on every billable response. Use the SDK's raw response accessor to read them:

const raw = await client.chat.completions
  .withResponse(client.chat.completions.create({ model: "anthropic/claude-sonnet-4-6", messages }));

// Simpler: use the .asResponse() pattern
const { data, response } = await client.chat.completions
  .create({ model: "anthropic/claude-sonnet-4-6", messages })
  .withResponse();

console.log("charged:", response.headers.get("X-MDB-Charged-USD"));
console.log("balance:", response.headers.get("X-MDB-Balance-USD"));
const reply = data.choices[0].message.content;

Printing the running balance is a nice touch for a developer tool, you always know how much credit is left.

Step 6: Make it robust

A real chatbot should handle the occasional failed request. Wrap the API call in a try/catch and retry on transient errors:

try {
  const resp = await client.chat.completions.create({ model, messages });
  // ...
} catch (err) {
  console.error("Request failed:", err.status, err.message);
}

A 401 means a bad key, a 402 means you are out of credit, and a 429 means you should back off and retry. Long conversations also grow the token count, so consider trimming old messages once history gets large.

Step 7: Swap models freely

Change the model string to route the same bot through openai/gpt-4o, google/gemini-2.0-flash, or mistralai/mistral-large. You could even let users switch models mid-conversation with a command.

You now have a working, memory-aware Node.js chatbot. Grab a key and add credit at your dashboard, and dig into streaming and parameters in the docs.

← All articles Get your API key →