[AIWriteSupporter] Node Express Server Example
Sample Node/Express server endpoints that proxy AI requests for the plugin.
Text AI
GPT — using @waylaidwanderer/fetch-event-source
modules/FetchEventSource.js:
const { fetch, Headers, Request, Response } = require('fetch-undici');
if (!globalThis.fetch) {
globalThis.fetch = fetch;
globalThis.Headers = Headers;
globalThis.Request = Request;
globalThis.Response = Response;
}
module.exports = require('@waylaidwanderer/fetch-event-source');
server.js:
const express = require('express');
const bodyParser = require('body-parser');
const { fetchEventSource } = require('./modules/FetchEventSource');
const GPT_API_URL = ''; // e.g. 'https://api.openai.com/v1/chat/completions'
const GPT_API_KEY = '';
const app = express();
const router = express.Router();
router.post('/request', (req, res) => {
const bodyData = Object.assign({}, req.body, {
model: 'gpt-3.5-turbo',
stream: true
});
requestGPT(res, bodyData)
.then(() => res.end())
.catch(error => res.status(error.status).json(error).end());
});
function requestGPT(res, bodyData) {
const abortController = new AbortController();
return new Promise(async (resolve, reject) => {
try {
await fetchEventSource(GPT_API_URL, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${GPT_API_KEY}` // OpenAI
// 'Api-Key': GPT_API_KEY // Azure OpenAI
},
body: JSON.stringify(bodyData),
signal: abortController.signal,
onopen: async (openResponse) => {
if (openResponse.status === 200) {
res.on('close', () => { abortController.abort(); resolve(); });
res.set({
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream'
});
res.flushHeaders();
return;
}
let error;
try { error = await openResponse.json(); }
catch (e) { error = e; }
error.status = openResponse.status;
throw error;
},
onclose: () => resolve(),
onerror: (error) => { reject(error); throw error; },
onmessage: (message) => {
if (!message.data || message.event === 'ping') return;
res.write('data:' + message.data + '\n\n');
}
});
} catch (e) { reject(e); }
});
}
app.use(bodyParser.json());
app.use('/', router);
app.listen(8080);
HyperCLOVA X — using axios
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const HCX_API_URL = ''; // e.g. 'https://clovastudio.apigw.ntruss.com/testapp/v1/chat-completions/HCX-003'
const HCX_API_KEY = '';
const HCX_GATEWAY_KEY = '';
const app = express();
const router = express.Router();
router.post('/request', (req, res) => {
requestHCX(res, req.body)
.then(() => res.end())
.catch(err => {
console.error('[ERROR] requestHCX chat completion failed:', err);
return res.status(err.status || 500).json(err).end();
});
});
function requestHCX(res, bodyData) {
const source = axios.CancelToken.source();
const headers = {
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
'X-NCP-CLOVASTUDIO-API-KEY': HCX_API_KEY,
'X-NCP-APIGW-API-KEY': HCX_GATEWAY_KEY
};
return new Promise(async (resolve, reject) => {
try {
axios.post(HCX_API_URL, bodyData, {
headers, responseType: 'stream', cancelToken: source.token
}).then(hcxRes => hcxRes.data.pipe(res)).catch(reject);
res.on('close', () => { source.cancel('Cancel request.'); resolve(); });
} catch (e) { reject(e); }
});
}
app.use(bodyParser.json());
app.use('/', router);
app.listen(8080);
Gemini — using axios
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const GEMINI_URL = ''; // e.g. 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:streamGenerateContent'
const GEMINI_PARAM = ''; // e.g. 'alt=sse&'
const GEMINI_KEY = '';
const app = express();
const router = express.Router();
router.post('/request', (req, res) => {
requestGeminiServer(res, req.body)
.then(() => res.end())
.catch(err => res.status(err.status || 500).json(err).end());
});
function requestGeminiServer(res, bodyData) {
const urlWithParam = `${GEMINI_URL}?${GEMINI_PARAM}key=${GEMINI_KEY}`;
const source = axios.CancelToken.source();
return new Promise((resolve, reject) => {
try {
axios.post(urlWithParam, bodyData, {
headers: { 'Content-Type': 'application/json' },
responseType: 'stream',
cancelToken: source.token
}).then(geminiRes => geminiRes.data.pipe(res)).catch(reject);
res.on('close', () => { source.cancel('Cancel request.'); resolve(); });
} catch (e) { reject(e); }
});
}
app.use(bodyParser.json());
app.use('/', router);
app.listen(8080);
Image AI — DALL·E (axios)
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const DALLE_URL = ''; // e.g. 'https://api.openai.com/v1/images/generations'
const DALLE_API_KEY = '';
const app = express();
const router = express.Router();
router.post('/request', (req, res) => {
requestDALLEServer(res, req.body)
.then(data => res.json(data))
.catch(error => res.status(error.status).json(error).end());
});
function requestDALLEServer(res, bodyData) {
const source = axios.CancelToken.source();
return new Promise((resolve, reject) => {
try {
axios.post(DALLE_URL, bodyData, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${DALLE_API_KEY}`
},
cancelToken: source.token
}).then(r => resolve(r.data)).catch(error => {
reject({
status: error.response ? error.response.status : 500,
message: error.message
});
});
res.on('close', () => { source.cancel('Cancel request.'); resolve(); });
} catch (e) { reject(e); }
});
}
app.use(bodyParser.json());
app.use('/', router);
app.listen(8080);