Skip to content
Fondazione ITS INCOM

Examples · Web Development

Your first API call in JavaScript

Install Node.js and the OpenAI library, put your key in an environment variable, and ask Qwen3.8 a question from a .mjs file, first in one piece and then streamed word by word. Your code checks that the key is there and that the answer was not cut off.

Beginner · 15 min

What you need

This is the JavaScript twin of Your first API call in Python. The other JavaScript examples start from here.

1. Create a key

At my.ai.itsincom.org, open API keys and create a key. Copy it straight away: it is shown only once.

A key is like a password: do not share it, and never put it in code you publish (GitHub included) or in a web page. Every call uses your credits.

2. A folder and the library

mkdir first-call
cd first-call
npm install openai

npm answers added 1 package and creates package.json and a node_modules folder. ITS INCOM AI speaks the same language as OpenAI: you use their library and change only the address and the key.

3. Put the key in an environment variable

macOS and Linux:

export API_KEY="sk-..."

Windows (PowerShell):

$env:API_KEY = "sk-..."

The variable lives only in that terminal window. Open a new one, and you set it again. Run the program from the same window where you set it.

4. The program

Save it as first.mjs in the first-call folder. The .mjs ending tells Node that the file is an ES module: it can use import, and await outside a function.

import OpenAI from "openai";

if (!process.env.API_KEY) {
  console.error("API_KEY is not set: see step 3.");
  process.exit(1);
}

const client = new OpenAI({
  baseURL: "https://api.ai.itsincom.org/v1",
  apiKey: process.env.API_KEY,
});

const answer = await client.chat.completions.create({
  model: "qwen3.8-27b",
  messages: [
    { role: "system", content: "Answer in English, briefly." },
    { role: "user", content: "Write a JavaScript function that checks whether a string is a palindrome." },
  ],
});

const choice = answer.choices[0];
console.log(choice.message.content.trim()); // qwen3.8-27b may start with blank lines
console.log("finish_reason:", choice.finish_reason);
console.log("tokens used:", answer.usage.total_tokens);

Run it:

node first.mjs

What you should see

The words change from run to run. One of ours, after about a second:

```javascript
function isPalindrome(str) {
  const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
  return cleaned === cleaned.split('').reverse().join('');
}
```

**How it works:**
1. Lowercases the string and strips non-alphanumeric characters (so `"A man, a plan, a canal: Panama"` works).
2. Compares the cleaned string to its reverse.

**Usage:**
```javascript
isPalindrome("racecar")      // true
isPalindrome("hello")        // false
isPalindrome("A man a plan a canal Panama") // true
```
finish_reason: stop
tokens used: 198

The answer is Markdown: the backticks and the stars are for a page that turns them into code boxes and bold text. In a terminal you see them as they are.

5. The same, streamed

Without streaming, your program waits in silence until the whole answer is ready. With stream: true, the answer arrives in small pieces, and you print each piece as it comes. This is what chat pages do.

Save it as stream.mjs:

import OpenAI from "openai";

if (!process.env.API_KEY) {
  console.error("API_KEY is not set: see step 3.");
  process.exit(1);
}

const client = new OpenAI({
  baseURL: "https://api.ai.itsincom.org/v1",
  apiKey: process.env.API_KEY,
});

const started = Date.now();
const stream = await client.chat.completions.create({
  model: "qwen3.8-27b",
  messages: [
    { role: "system", content: "Answer in English, briefly." },
    { role: "user", content: "Write a JavaScript function that checks whether a string is a palindrome." },
  ],
  stream: true,
});

let written = "";
let firstWords = null;
let finish = null;

for await (const chunk of stream) {
  const choice = chunk.choices[0];
  let text = choice.delta.content ?? "";
  if (written === "") text = text.trimStart(); // skip the blank lines at the start
  if (text && firstWords === null) firstWords = Date.now() - started;
  process.stdout.write(text);
  written += text;
  if (choice.finish_reason) finish = choice.finish_reason;
}

console.log(`\n\nfirst words after ${firstWords} ms, the whole answer after ${Date.now() - started} ms`);
console.log("finish_reason:", finish);

Run it with node stream.mjs. The text appears a few words at a time. At the end, one of our runs said:

first words after 285 ms, the whole answer after 792 ms
finish_reason: stop

A short answer is ready in under a second either way. A long one is where streaming matters: when we asked for “about 400 words on how a web browser loads a page”, the first words came after 0.8–0.9 seconds, and the whole answer after 10 to 16 seconds. Without streaming, the same question meant 12 seconds of silence.

What the program checks

Good to know

Exercise

All the details are in the documentation.

All examples