Skip to content
WP EngineDocumentation

Node.js

These samples use the built-in fetch shipped with Node 18+, so they have no third-party dependencies. Chat completion reads the API key from the POWER_API_KEY environment variable. Both functions throw on a non-2xx response — wrap calls in try/catch in real code.

Set the key once in your shell before calling authenticated endpoints:

Terminal window
export POWER_API_KEY="wpe_xxx"

A lightweight way to discover model identifiers you can pass to the chat endpoint. No authentication is required.

const BASE_URL = "https://api.ai.wpengine.com";
async function listModels() {
const res = await fetch(`${BASE_URL}/v1/models`);
if (!res.ok) {
throw new Error(`models request failed: ${res.status}`);
}
return res.json();
}

A standard non-streaming chat completion. The full response — including generated content and token usage — is returned as a single JSON object once the model finishes.

const BASE_URL = "https://api.ai.wpengine.com";
async function chatCompletion(prompt) {
const res = await fetch(`${BASE_URL}/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POWER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/gemini-3.5-flash",
messages: [
{ role: "system", content: "You are concise." },
{ role: "user", content: prompt },
],
}),
});
if (!res.ok) {
const body = await res.text();
throw new Error(`chat request failed: ${res.status} ${body}`);
}
return res.json();
}

Set "stream": true to receive the response as Server-Sent Events (SSE) instead of a single JSON payload. This is the right shape when you want to render tokens to the UI as they’re produced.

SSE chunks don’t arrive aligned on event boundaries. A single TCP read may contain several complete data: lines plus a partial line, or just a fragment of one. The loop below handles that by:

  1. Decoding each chunk into text with a streaming TextDecoder, which preserves multi-byte UTF-8 sequences across chunk boundaries.
  2. Buffering decoded text and splitting on \n. The final element of the split is held back as the next iteration’s prefix in case the chunk ended mid-line.
  3. Processing each complete line: lines starting with data: carry a JSON chat.completion.chunk. The sentinel data: [DONE] signals that the model has finished and the stream can be closed.

The onDelta callback is invoked once per parsed chunk so the caller decides what to do with each delta (append to a buffer, write to stdout, push to a WebSocket, etc.).

const BASE_URL = "https://api.ai.wpengine.com";
async function chatStream(prompt, onDelta) {
const res = await fetch(`${BASE_URL}/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POWER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/gemini-3.5-flash",
stream: true,
messages: [{ role: "user", content: prompt }],
}),
});
if (!res.ok || !res.body) {
throw new Error(`stream request failed: ${res.status}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() ?? "";
for (const line of lines) {
if (!line.startsWith("data: ")) continue;
const data = line.slice(6).trim();
if (data === "[DONE]") return;
onDelta(JSON.parse(data));
}
}
}

Pass a prompt and a callback that handles each delta. This example streams tokens to stdout as they arrive:

await chatStream("Explain how a CDN works in about 100 words.", (chunk) => {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
});

Last updated: