跳转到内容

搭建服务器 ​

刀刀
0字
0分钟
10/23/2026
nodejsexpressopenaiserver
从零搭建 Node.js Express 服务器代理大模型 API 请求,涵盖 OpenAI SDK 使用、fetch 拦截调试、消息历史管理及流式传输基础。

前言 ​

前端虽然可以直接请求大模型接口,但存在以下问题:

  1. API Key 写在前端容易暴露
  2. 无法鉴权和拦截攻击
  3. 后续操作如定义工具、MCP 等无法进行

OpenAI 库 ​

主流编程语言都有专门调用 AI 接口的库,且基本遵循同一套标准。如 Node.js 的 OpenAI 库。

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: [{ role: 'user', content: keyword }],
  })
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

详解 OpenAI ​

  1. OpenAI 库使用的地址通常与直接 HTTP 请求不同
  2. OpenAI 库相对于 HTTP 请求,提供了规范,更便于使用 Function Call 及 MCP 接入,代码更简洁,优先选用。此外还提供其他方法用于其他场景,等同于一个专门用来请求大模型接口的 SDK
  3. OpenAI 库请求接口本质上仍用 fetch 发送 HTTP 请求,可拦截其发出的 HTTP 请求和响应

重写 fetch 查看请求和响应 ​

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

// 重写 fetch,让整个项目通过 fetch 发请求时会打印一些东西
const originalFetch = global.fetch 
global.fecth = async function (...args) {
  const [url, options = {}] = args 
  console.log('fetching', url, options) 
  try {
    const response = await originalFetch.apply(this, args) // 调用原始 fetch
    const clonedResponse = response.clone() // 克隆响应以便读取body而不影响原始响应
    for (const [key, value] of Object.entries(options.headers)) {
      console.log(`${key}: ${value}`) 
    } 
    const result = await clonedResponse.json() // 尝试读取响应体
    return response 
  } catch (error) {
    console.error(`Error: ${error}`) 
  } 
} 

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: [{ role: 'user', content: keyword }],
  })
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

携带历史 ​

接口是无状态的,每次请求携带上下文有助于模型理解用户意图。实现一个简单的携带历史功能。

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

// 重写 fetch,让整个项目通过 fetch 发请求时会打印一些东西
const originalFetch = global.fetch

global.fecth = async function (...args) {
  const [url, options = {}] = args
  console.log('fetching', url, options)

  try {
    const response = await originalFetch.apply(this, args) // 调用原始 fetch
    const clonedResponse = response.clone() // 克隆响应以便读取body而不影响原始响应

    for (const [key, value] of Object.entries(options.headers)) {
      console.log(`${key}: ${value}`)
    }
    const result = await clonedResponse.json() // 尝试读取响应体
    return response
  } catch (error) {
    console.error(`Error: ${error}`)
  }
}

const messageList = [] // 存储历史消息

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const queryObj = {
    role: 'user', 
    content: keyword, 
  } 
  messageList.push(queryObj) // 每次提问保存上下文
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: messageList,
  })
  messageList.push(aiRes.choices[0].message) // 每次回答保存上下文
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

总结 ​

贡献者

The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as duyidao duyidao
The avatar of contributor named as Copilot Copilot

页面历史

刀刀博客累计访客 人;文档累计访问量共 次