Official openai npm package Tokens-এর সাথে চালাতে দুটো setting বদলালেই হয়: baseURL আর apiKey। এই পেজে আছে TypeScript setup, streaming, APIError থেকে Tokens-এর error code পড়া, আর একটা Next.js route handler, যাতে আপনার key কখনো browser-এ না পৌঁছায়।
OpenAI Node.js SDK install করুন#
npm install openai
export TOKENS_API_KEY="tok_live_your_key"উদাহরণগুলোতে ES module আর top-level await আছে, তাই .mjs বা .ts file হিসেবে চালান (যেমন npx tsx hello.ts)। ধরে নেওয়া হয়েছে আপনি openai-এর সাম্প্রতিক major version (v5 বা তার পরের) ব্যবহার করছেন।
import OpenAI from "openai";
export const tokens = new OpenAI({
baseURL: "https://tokens.bd/v1",
apiKey: process.env.TOKENS_API_KEY,
timeout: 120_000, // ms; SDK default is 10 minutes
maxRetries: 2, // SDK default; retries connection errors, 408, 409, 429, 5xx
});import { tokens } from "./lib/tokens";
const completion = await tokens.chat.completions.create({
model: "deepseek/deepseek-v4.1-flash",
messages: [
{ role: "system", content: "Reply in one short paragraph." },
{ role: "user", content: "What's the difference between interface and type in TypeScript?" },
],
max_tokens: 400,
});
console.log(completion.choices[0]?.message.content);
console.log(completion.usage);Code না ছুঁয়েই কাজ সারতে চাইলে SDK environment থেকে OPENAI_BASE_URL আর OPENAI_API_KEY-ও পড়ে নেয়। তবে খেয়াল রাখবেন, ওই environment-এ যত OpenAI-based tool আছে, এই variable সবগুলোর ওপরই প্রভাব ফেলবে।
Model ID copy করুন /models থেকে বা await tokens.models.list() থেকে। আন্দাজে লিখবেন না।
Chat completion stream করুন#
const stream = await tokens.chat.completions.create({
model: "deepseek/deepseek-v4.1-flash",
messages: [{ role: "user", content: "List five git commands I should know, one per line." }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content;
if (text) process.stdout.write(text);
if (chunk.usage) {
console.log(`\n${chunk.usage.prompt_tokens} in, ${chunk.usage.completion_tokens} out`);
}
}include_usage চালু থাকলে শেষ chunk-এর choices array ফাঁকা থাকে, সেজন্যই code-এ optional chaining আছে। মাঝপথে থামাতে চাইলে loop থেকে break করুন অথবা stream.controller.abort() call করুন। আরও দেখুন Streaming।
APIError দিয়ে error সামলান#
2xx ছাড়া যেকোনো response OpenAI.APIError-এর একটা subclass throw করে। Tokens প্রতিটি error-এ একটা নির্দিষ্ট code বসিয়ে দেয়, আর HTTP status-এর চেয়ে সেটা থেকে অনেক বেশি জানা যায়।
import OpenAI from "openai";
import { tokens } from "./lib/tokens";
try {
await tokens.chat.completions.create({
model: "deepseek/deepseek-v4.1-flash",
messages: [{ role: "user", content: "hi" }],
});
} catch (err) {
if (err instanceof OpenAI.APIConnectionError) {
console.error("Network problem:", err.message);
} else if (err instanceof OpenAI.APIError) {
const requestId = err.headers?.get("x-tokens-request-id");
console.error(err.status, err.code, err.message, requestId);
switch (err.code) {
case "insufficient_credits":
// 402: top up at /dashboard/billing
break;
case "window_exhausted":
// 429: plan window used up; Retry-After header = seconds until reset
console.error("Resets in", err.headers?.get("retry-after"), "s");
break;
case "model_not_allowed_on_key":
case "tier_permission_denied":
// 403: key allow-list or plan doesn't cover this model
break;
}
} else {
throw err;
}
}দুটো কথা জেনে রাখুন:
- SDK-র সাম্প্রতিক version-এ
err.headersএকটা সাধারণHeadersobject, তাই.get()দিয়ে পড়ুন।x-tokens-request-idসবসময় log করুন। আপনার request খুঁজে পেতে Support-এর এটা লাগে। - SDK নিজে থেকেই
429আর5xx-এ retry করে। আর502,503বা504ফেরত দেওয়ার আগেই gateway অন্য upstream source-এ চেষ্টা করে দেখে নিয়েছে।window_exhausted-এRetry-Afterপার না হওয়া পর্যন্ত retry করে লাভ নেই, আর সেটা কয়েক ঘণ্টাও হতে পারে। তাই তাড়াতাড়ি fail করাতে চাইলেmaxRetriesকম রাখুন।
সব code-এর তালিকা Errors পেজে, আর প্রতিটার সমাধান Troubleshooting পেজে।
Key server-এ রাখুন: Next.js route handler#
Tokens CORS header পাঠায় না, তাই browser-এর JavaScript থেকে সরাসরি call করলে সেটা fail করবে। আর করলেও আপনার key ফাঁস হয়ে যেত। Call-টা একটা route handler-এর ভেতরে রাখুন, আর frontend থেকে সেই route-কে call করুন।
import OpenAI from "openai";
export const runtime = "nodejs";
const tokens = new OpenAI({
baseURL: "https://tokens.bd/v1",
apiKey: process.env.TOKENS_API_KEY, // server-only: no NEXT_PUBLIC_ prefix
});
type ChatMessage = { role: "user" | "assistant"; content: string };
export async function POST(req: Request) {
const body = (await req.json()) as { messages?: ChatMessage[] };
if (!Array.isArray(body.messages) || body.messages.length === 0) {
return Response.json({ error: "messages required" }, { status: 400 });
}
try {
const stream = await tokens.chat.completions.create(
{
model: "deepseek/deepseek-v4.1-flash", // pick on the server, not from the client
messages: body.messages.slice(-20),
max_tokens: 1024,
stream: true,
},
{ signal: req.signal } // stop paying for tokens if the user navigates away
);
const encoder = new TextEncoder();
const text = new ReadableStream<Uint8Array>({
async start(controller) {
try {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) controller.enqueue(encoder.encode(delta));
}
controller.close();
} catch (e) {
controller.error(e);
}
},
});
return new Response(text, {
headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" },
});
} catch (err) {
if (err instanceof OpenAI.APIError) {
return Response.json(
{ error: err.code ?? "upstream_error", requestId: err.headers?.get("x-tokens-request-id") },
{ status: err.status ?? 502 }
);
}
throw err;
}
}Client-এর দিকে লাগে শুধু সাধারণ একটা fetch, যা response body-কে stream হিসেবে পড়ে:
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages: [{ role: "user", content: "Hello" }] }),
});
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
const { value, done } = await reader.read();
if (done) break;
console.log(value);
}Handler-এর কিছু সিদ্ধান্ত ইচ্ছে করেই নেওয়া। Model আর max_tokens server-এ ঠিক করা হয়েছে, যাতে কোনো visitor আপনার app-কে দামি model-এ নিয়ে যেতে না পারে। Message history ছেঁটে রাখা হয়েছে। এই route-এর সামনে নিজের authentication আর প্রতি user-এর rate limiting বসানোও উচিত, কারণ যে-ই route-টায় পৌঁছাতে পারবে, সে-ই আপনার ব্যালান্স খরচ করবে। মাসিক spend cap দেওয়া key (API keys) ক্ষতির একটা শক্ত ঊর্ধ্বসীমা বেঁধে দেয়।
Vercel AI SDK ব্যবহার করলে streamText দিয়ে একই pattern Vercel AI SDK পেজে দেখানো আছে।