Bun + TypeScript 后端入门全记录:从类型约束到 LLM API 调用
一份零基础后端学习笔记,覆盖 Bun 运行时、TypeScript 类型系统、HTTP 请求封装,以及用 Axios 调用大模型接口的完整流程。
目录
- [1. Bun:不只是更快的 Node.js](#1. Bun:不只是更快的 Node.js "#1-bun%E4%B8%8D%E5%8F%AA%E6%98%AF%E6%9B%B4%E5%BF%AB%E7%9A%84-nodejs")
- [1.1 Bun 是什么](#1.1 Bun 是什么 "#11-bun-%E6%98%AF%E4%BB%80%E4%B9%88")
- [1.2 Bun 的三重身份](#1.2 Bun 的三重身份 "#12-bun-%E7%9A%84%E4%B8%89%E9%87%8D%E8%BA%AB%E4%BB%BD")
- [1.3 Bun 与 Anthropic / Claude Code](#1.3 Bun 与 Anthropic / Claude Code "#13-bun-%E4%B8%8E-anthropic--claude-code")
- [1.4 安装 Bun](#1.4 安装 Bun "#14-%E5%AE%89%E8%A3%85-bun")
- [2. TypeScript:给 JavaScript 装上类型锚点](#2. TypeScript:给 JavaScript 装上类型锚点 "#2-typescript%E7%BB%99-javascript-%E8%A3%85%E4%B8%8A%E7%B1%BB%E5%9E%8B%E9%94%9A%E7%82%B9")
- [2.1 JavaScript 的"弱类型"之痛](#2.1 JavaScript 的"弱类型"之痛 "#21-javascript-%E7%9A%84%E5%BC%B1%E7%B1%BB%E5%9E%8B%E4%B9%8B%E7%97%9B")
- [2.2 TypeScript 如何解决问题](#2.2 TypeScript 如何解决问题 "#22-typescript-%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E9%97%AE%E9%A2%98")
- [2.3 基础类型注解](#2.3 基础类型注解 "#23-%E5%9F%BA%E7%A1%80%E7%B1%BB%E5%9E%8B%E6%B3%A8%E8%A7%A3")
- [2.4 函数签名中的类型约束](#2.4 函数签名中的类型约束 "#24-%E5%87%BD%E6%95%B0%E7%AD%BE%E5%90%8D%E4%B8%AD%E7%9A%84%E7%B1%BB%E5%9E%8B%E7%BA%A6%E6%9D%9F")
- [2.5 AI 时代为什么 TypeScript 成了主流](#2.5 AI 时代为什么 TypeScript 成了主流 "#25-ai-%E6%97%B6%E4%BB%A3%E4%B8%BA%E4%BB%80%E4%B9%88-typescript-%E6%88%90%E4%BA%86%E4%B8%BB%E6%B5%81")
- [3. 类型转换实战:三种方式把字符串变成数字](#3. 类型转换实战:三种方式把字符串变成数字 "#3-%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2%E5%AE%9E%E6%88%98%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%E6%8A%8A%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%98%E6%88%90%E6%95%B0%E5%AD%97")
- [3.1
Number():显式构造函数](#3.1 Number():显式构造函数 "#31-number%E6%98%BE%E5%BC%8F%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0") - [3.2
parseInt():解析整数](#3.2 parseInt():解析整数 "#32-parseint%E8%A7%A3%E6%9E%90%E6%95%B4%E6%95%B0") - [3.3 一元加运算符
+:隐式转换](#3.3 一元加运算符 +:隐式转换 "#33-%E4%B8%80%E5%85%83%E5%8A%A0%E8%BF%90%E7%AE%97%E7%AC%A6-%E9%9A%90%E5%BC%8F%E8%BD%AC%E6%8D%A2") - [3.4 三种方式的对比](#3.4 三种方式的对比 "#34-%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%E7%9A%84%E5%AF%B9%E6%AF%94")
- [3.1
- [4. 浏览器中的类型陷阱:一个 Input 事件的教训](#4. 浏览器中的类型陷阱:一个 Input 事件的教训 "#4-%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E7%9A%84%E7%B1%BB%E5%9E%8B%E9%99%B7%E9%98%B1%E4%B8%80%E4%B8%AA-input-%E4%BA%8B%E4%BB%B6%E7%9A%84%E6%95%99%E8%AE%AD")
- [5. 封装异步工具:手写 sleep 函数](#5. 封装异步工具:手写 sleep 函数 "#5-%E5%B0%81%E8%A3%85%E5%BC%82%E6%AD%A5%E5%B7%A5%E5%85%B7%E6%89%8B%E5%86%99-sleep-%E5%87%BD%E6%95%B0")
- [6. HTTP 请求:从 fetch 到 axios](#6. HTTP 请求:从 fetch 到 axios "#6-http-%E8%AF%B7%E6%B1%82%E4%BB%8E-fetch-%E5%88%B0-axios")
- [6.1 GET 请求的局限](#6.1 GET 请求的局限 "#61-get-%E8%AF%B7%E6%B1%82%E7%9A%84%E5%B1%80%E9%99%90")
- [6.2 POST 请求的结构](#6.2 POST 请求的结构 "#62-post-%E8%AF%B7%E6%B1%82%E7%9A%84%E7%BB%93%E6%9E%84")
- [6.3 fetch vs axios:原生 API 与封装框架的差距](#6.3 fetch vs axios:原生 API 与封装框架的差距 "#63-fetch-vs-axios%E5%8E%9F%E7%94%9F-api-%E4%B8%8E%E5%B0%81%E8%A3%85%E6%A1%86%E6%9E%B6%E7%9A%84%E5%B7%AE%E8%B7%9D")
- [7. 实战:用 Bun + Axios 调用 DeepSeek 大模型](#7. 实战:用 Bun + Axios 调用 DeepSeek 大模型 "#7-%E5%AE%9E%E6%88%98%E7%94%A8-bun--axios-%E8%B0%83%E7%94%A8-deepseek-%E5%A4%A7%E6%A8%A1%E5%9E%8B")
- [7.1 项目初始化](#7.1 项目初始化 "#71-%E9%A1%B9%E7%9B%AE%E5%88%9D%E5%A7%8B%E5%8C%96")
- [7.2 环境变量管理](#7.2 环境变量管理 "#72-%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E7%AE%A1%E7%90%86")
- [7.3 编写请求代码](#7.3 编写请求代码 "#73-%E7%BC%96%E5%86%99%E8%AF%B7%E6%B1%82%E4%BB%A3%E7%A0%81")
- [7.4 逐行拆解](#7.4 逐行拆解 "#74-%E9%80%90%E8%A1%8C%E6%8B%86%E8%A7%A3")
- [7.5 运行与调试](#7.5 运行与调试 "#75-%E8%BF%90%E8%A1%8C%E4%B8%8E%E8%B0%83%E8%AF%95")
- [8. 总结](#8. 总结 "#8-%E6%80%BB%E7%BB%93")
- 感谢阅读
1. Bun:不只是更快的 Node.js
1.1 Bun 是什么
Bun 是一个JavaScript / TypeScript 运行时,由 Jarred Sumner 使用 Zig 语言从零编写。它的设计目标很直白------做一个比 Node.js 更快、更现代、且开箱即用的替代品。
如果你写过 Node.js,你一定经历过这些事:装 nodemon 做热重载、装 ts-node 或 tsx 跑 TypeScript、装 dotenv 读环境变量、面对 node_modules 黑洞般的体积叹气。Bun 把这些痛点打包解决掉了------它原生支持 TypeScript,内置文件监听热重载,自带 .env 解析,包安装速度比 npm 快一个数量级。
性能方面,Bun 使用 JavaScriptCore(Safari 的 JS 引擎)替代 V8,启动速度极快,尤其适合短生命周期任务(如 Serverless 函数、脚本工具)。
1.2 Bun 的三重身份
Bun 同时扮演三个角色:
| 角色 | 对标工具 | 说明 |
|---|---|---|
| 运行时 | Node.js | 执行 JS/TS 代码,提供标准 API |
| 包管理器 | npm / yarn / pnpm | bun install 安装依赖,速度更快 |
| 打包器 | Webpack / esbuild | bun build 打包项目,原生速度 |
你不需要分别安装运行环境和包管理工具,一个 bun 命令全部搞定。
1.3 Bun 与 Anthropic / Claude Code
2025 年底,Anthropic 收购了 Bun 的开发团队 Oven。这意味着 Bun 的底层技术被直接用于 Claude Code 的运行环境。当你使用 Claude Code 执行代码时,底层跑的可能就是 Bun。这一收购也说明了 Bun 在 AI 工具链中的战略地位------AI Agent 需要快速启动、快速执行、快速迭代的运行环境,而 Bun 恰好擅长这些。
1.4 安装 Bun
Windows 下通过 PowerShell 安装:
powershell
powershell -c "irm bun.sh/install.ps1 | iex"
macOS / Linux:
bash
curl -fsSL https://bun.sh/install | bash
安装完成后用 bun --version 验证。
2. TypeScript:给 JavaScript 装上类型锚点
2.1 JavaScript 的"弱类型"之痛
JavaScript 是弱类型语言,变量的类型在运行时可以随意变化。这带来了灵活性,也埋下了无数 bug。来看一个经典场景:
javascript
const a = 1;
const b = "2";
console.log(a + b); // "12" ------ 字符串拼接,不是数学加法
+ 运算符在 JS 里有双重含义:数学加法和字符串拼接。当一侧操作数是字符串时,JS 会把另一边也转成字符串然后拼接。这个行为不会报错,所以你得不到任何异常提示------代码安静地产出了一个错误结果。在大项目中,这种静默的类型错误追查成本极高。
另一个常见陷阱在浏览器里:用户输入框的值永远是字符串,即使你期望的是一个数字。
html
<input type="number" id="age">
<script>
const age = document.getElementById("age").value; // 类型是 string,不是 number
console.log(age + 1); // "181" 而非 19
</script>
2.2 TypeScript 如何解决问题
TypeScript 是微软开发的 JavaScript 超集 ------所有合法的 JS 代码都是合法的 TS 代码,但 TS 额外增加了一层类型系统。
核心思路是:在编译阶段就发现问题,而不是等到运行时爆炸。
TypeScript 代码需要编译成 JavaScript 才能运行。编译过程做了两件事:
- 类型检查:扫描代码中的类型错误,不符合约束的直接报错
- 代码转换:移除类型注解,输出纯 JavaScript
关键一点:编译后的 JS 代码里没有任何 TypeScript 依赖,运行时开销为零。
2.3 基础类型注解
TypeScript 的类型注解写在变量名后面,用冒号分隔:
typescript
const nickname: string = "9527";
const age: number = 18;
console.log(`我是 ${nickname},今年 ${age} 岁`);
这行代码告诉编译器三件事:
nickname只能是stringage只能是number- 如果有人给
age赋了一个字符串,编译器直接拒绝
类型注解不是强制的------TS 有类型推断能力,很多场景下你不写注解它也能自动推出来。但显式写出来,相当于给代码加了文档,AI 和人类读者都更容易理解意图。
2.4 函数签名中的类型约束
函数的参数和返回值也可以标注类型:
typescript
function add(a: number, b: number): number {
return a + b;
}
这里的含义:
a: number--- 参数 a 必须是数字b: number--- 参数 b 必须是数字): number--- 返回值必须是数字
如果你尝试传入字符串,编辑器会在你保存文件之前就标红。TypeScript 的静态检查让"类型不对"这个类别的 bug 在编码阶段就被消灭。
2.5 AI 时代为什么 TypeScript 成了主流
当下几乎所有主流 AI 编码工具(Claude Code、GitHub Copilot、Cursor 等)都以 TypeScript 作为一等公民。原因有三:
- 类型信息是 AI 的说明书。AI 模型读代码时,类型注解告诉它每个变量是什么、函数接收什么返回什么,大幅减少理解偏差。
- 类型约束缩小了生成空间。AI 生成代码时有了类型约束,产出更精确,幻觉更少。
- 编译期错误拦截降低了 AI 代码的风险。即使用 AI 生成了类型不匹配的代码,编译器也会立刻拦住,形成了"AI 生成 → TS 编译检查 → 人工审核"的安全链路。
换句话说,TypeScript 不仅是给人用的,更是给 AI Agent 用的------复杂的项目中,类型系统是管理庞大数据结构的基础设施。
3. 类型转换实战:三种方式把字符串变成数字
在实际开发中,"外部数据是字符串,内部需要数字" 的场景非常频繁。下面用一段代码演示三种转换方式。
typescript
let a: number = 1;
let b: string = '2';
// 方式一:Number() 构造函数
console.log(add(a, Number(b))); // 3 ✅
// 方式二:parseInt() API
console.log(add(a, parseInt(b))); // 3 ✅
// 方式三:一元加运算符(隐式转换)
console.log(add(a, +b)); // 3 ✅
3.1 Number():显式构造函数
Number(value) 把任意值转成数字类型。规则清晰:能转就转,转不了返回 NaN。
typescript
Number('2'); // 2
Number('2.5'); // 2.5
Number('abc'); // NaN
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN
适用场景:需要转换小数、布尔值等任意类型到数字时。这是最通用的方式。
3.2 parseInt():解析整数
parseInt(string, radix) 从字符串开头逐个字符解析,遇到非数字字符就停止。
typescript
parseInt('2'); // 2
parseInt('2.9'); // 2(直接截断,不舍入)
parseInt('10px'); // 10(遇到 'p' 停止)
parseInt('abc'); // NaN(第一个字符就不是数字)
parseInt('0xFF'); // 255(自动识别十六进制)
注意 :始终传第二个参数 radix,避免意外行为。
typescript
parseInt('08', 10); // 8,明确指定十进制
parseInt('0x10', 16); // 16,十六进制
适用场景 :从带单位的字符串里提取整数,如 "10px" → 10。
3.3 一元加运算符 +:隐式转换
在变量前加一个 + 号,本质上调用的是 ToNumber 内部操作,效果和 Number() 几乎一致。
typescript
+'2'; // 2
+'2.5'; // 2.5
+'abc'; // NaN
+true; // 1
优点:写法最简洁,常用于快速转换。
缺点 :代码意图不够显式,不熟悉的人可能疑惑 "为什么前面有个加号"。如果你的团队规范要求代码可读性优先,建议用 Number() 替代。
3.4 三种方式的对比
| 方式 | 小数支持 | 非纯数字字符串 | 可读性 | 推荐场景 |
|---|---|---|---|---|
Number() |
✅ | 返回 NaN | ⭐⭐⭐ | 通用转换,最推荐 |
parseInt() |
❌ 截断 | 逐个解析,遇非数字停止 | ⭐⭐ | 提取整数("10px" 等) |
+ |
✅ | 返回 NaN | ⭐ | 快速转换,团队风格允许时使用 |
4. 浏览器中的类型陷阱:一个 Input 事件的教训
来看一段看似无害的 HTML + JS 代码:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="ipt">
<script>
const ipt = document.getElementById("ipt");
ipt.addEventListener("change", function () {
console.log(event.target.value, typeof event.target.value);
})
</script>
</body>
</html>
这段代码的日志输出会让你意外------无论 <input> 的 type 属性是 "text" 还是 "number",event.target.value 返回的始终是 string。
javascript
// 用户在输入框输入 123
console.log(typeof event.target.value); // "string",不是 "number"
这是因为 HTML 规范定义 input.value 就是字符串类型,type="number" 只在 UI 层限制了输入键盘和表单验证,并不改变底层 DOM API 的返回类型。
如果你直接用这个值做数学运算:
javascript
const price = document.getElementById("price").value; // "100"
const total = price * 1.1; // 110,意外的正确------因为 * 运算符强制触发数字转换
const sum = price + 10; // "10010" ------ 字符串拼接!
*、-、/ 运算符只有数学含义,会自动把字符串转成数字;但 + 同时有字符串拼接的含义,导致结果不可预期。这就是 TypeScript 要消灭的"隐式行为 bug"------如果你在 TS 中给 add(a: number, b: number) 传入 event.target.value,编译器会直接报错,因为你试图把 string 传给 number。
教训:永远不要信任 DOM 的输入类型,拿到值后先做显式转换。
5. 封装异步工具:手写 sleep 函数
JavaScript 里没有原生的 sleep(),但可以用 Promise 封装一个:
javascript
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
console.log('--start--');
await sleep(2000); // 暂停 2 秒
console.log('--end--');
}
拆解这段代码:
new Promise(resolve => ...):创建一个 Promise 对象。Promise 是 JS 异步的核心抽象------代表一个"未来会完成"的操作。setTimeout(resolve, ms):ms毫秒后调用resolve,也就是告诉 Promise "我完成了"。await sleep(2000):await暂停当前 async 函数的执行,直到 Promise 完成。期间 JS 引擎不会阻塞,可以去处理其他任务。
这个模式在调用 LLM API 时特别有用------比如请求频率限制时需要等待,或者轮询异步任务结果时需要间隔。
6. HTTP 请求:从 fetch 到 axios
6.1 GET 请求的局限
HTTP GET 请求把参数拼接在 URL 里:
ini
https://api.example.com/chat?message=hello&apikey=sk-xxxxx
这带来了几个问题:
- API Key 暴露在 URL 中。URL 会被浏览器历史记录、服务器日志、代理服务器完整记录,第三方拿到 URL 就拿到了你的密钥。
- 长度限制。URL 有最大长度限制(浏览器通常是 2048 字符,不同服务器实现差异很大),发送长文本(比如一篇完整的对话上下文)会超出限制。
- 语义不匹配。GET 的语义是"获取资源",不应该产生副作用。但调用 LLM 显然有副作用。
6.2 POST 请求的结构
POST 请求把数据放在请求体(body)里,URL 保持干净:
bash
POST https://api.deepseek.com/v1/chat/completions
Content-Type: application/json
Authorization: Bearer sk-xxxxx
{"model": "deepseek-v4-pro", "messages": [...]}
HTTP 请求报文由三部分组成:
| 部分 | 内容 | 示例 |
|---|---|---|
| 请求行 | Method + URL + HTTP 版本 | POST /v1/chat/completions HTTP/1.1 |
| 请求头 | 元信息:内容类型、认证、缓存控制等 | Content-Type: application/json |
| 请求体 | 实际数据(GET 请求没有这一部分) | JSON 字符串 |
6.3 fetch vs axios:原生 API 与封装框架的差距
浏览器提供了 fetch() API 发送 HTTP 请求,但它非常底层:
javascript
// 原生 fetch ------ 需要手动处理每一步
fetch('https://api.example.com/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer xxx' },
body: JSON.stringify({ message: 'hello' })
})
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json(); // 手动解析 JSON
})
.then(data => console.log(data))
.catch(err => console.error(err));
axios 在 fetch 之上做了封装:
javascript
// axios ------ 自动处理 JSON 和错误
const res = await axios.post('https://api.example.com/chat',
{ message: 'hello' }, // 自动 JSON.stringify
{ headers: { 'Authorization': 'Bearer xxx' } }
);
console.log(res.data); // 自动 JSON.parse
| 特性 | fetch | axios |
|---|---|---|
| JSON 自动序列化 | ❌ 需手动 JSON.stringify |
✅ 自动 |
| JSON 自动解析 | ❌ 需手动 .json() |
✅ res.data 直接可用 |
| 错误状态码处理 | ❌ 4xx/5xx 不会 reject | ✅ 自动抛异常 |
| 请求拦截器 | ❌ | ✅ |
| 响应拦截器 | ❌ | ✅ |
| 超时设置 | ❌ 需 AbortController | ✅ { timeout: 5000 } |
对于调用 LLM API 这种场景,axios 省去了大量样板代码,这也是为什么大多数后端项目选择 axios 而非裸 fetch。
7. 实战:用 Bun + Axios 调用 DeepSeek 大模型
这是笔记中最完整的实战代码,演示了从零搭建一个调用 LLM 的后端脚本。
7.1 项目初始化
bash
mkdir axios-demo && cd axios-demo
bun init # 初始化 Bun 项目
bun add axios dotenv # 安装依赖
bun add -d @types/node # 安装 Node 类型定义(开发依赖)
Bun 的包安装速度非常快------因为 Bun 的包管理器也是用 Zig 写的,比 npm 的 JS 实现快一个数量级。
7.2 环境变量管理
创建 .env 文件存储敏感信息:
env
llm_api_url=https://api.deepseek.com/v1/chat/completions
llm_api_key=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx
注意 :.env 文件一定不能提交到 Git。项目的 .gitignore 中应该有:
bash
.env
.env.local
.env.*.local
原因很简单:API Key 等于你的钱包钥匙,泄露后别人可以盗刷你的额度。
7.3 编写请求代码
typescript
import axios from "axios";
import dotenv from "dotenv";
dotenv.config(); // 加载 .env 文件到 process.env
async function chat() {
try {
const res = await axios.post(
`${process.env.llm_api_url}`, // 第1个参数:URL
{ // 第2个参数:请求体
model: 'deepseek-v4-pro',
messages: [
{ role: 'user', content: '介绍一下bun' }
]
},
{ // 第3个参数:请求配置
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.llm_api_key}`
}
}
);
console.log(res.data.choices[0].message.content);
} catch (error) {
console.log(error.message);
}
}
chat();
7.4 逐行拆解
import axios from "axios"
导入 axios 库。Bun 原生支持 ES Module 的 import 语法,不需要配置 "type": "module"。
import dotenv from "dotenv"
导入 dotenv 库。调用 dotenv.config() 后,.env 文件中的键值对会被注入到 process.env 对象中。之后可以通过 process.env.llm_api_url 访问。
axios.post(url, data, config)
axios 的 POST 方法接收三个参数:
url:请求地址。这里从环境变量读取,方便切换不同 LLM 服务商(OpenAI / DeepSeek / 本地模型等)。data:请求体对象。axios 自动将其序列化为 JSON 字符串。config:请求配置。在这里设置了两个关键 header。
Content-Type: application/json
告诉服务器"我发过来的请求体是 JSON 格式"。服务器收到后会按 JSON 解析,而不是当成表单或纯文本。
Authorization: Bearer ${process.env.llm_api_key}
Bearer Token 认证。Bearer 是"持有者"的意思------谁持有这个 Token,谁就拥有对应的访问权限。API Key 从环境变量读取,绝不硬编码在代码里。
res.data.choices[0].message.content
OpenAI 兼容接口的响应结构:
less
res.data // 整个响应体
└── choices: [] // 模型返回的候选回复列表(通常取第一个)
└── [0]
└── message: {} // 消息对象
└── content: "" // 模型回复的文本内容
理解这个嵌套结构,你就能从任何 OpenAI 兼容接口(DeepSeek、通义千问、GLM 等)中提取回复文本。
try...catch 错误处理
网络请求可能失败:网络断开、API Key 过期、服务商限流、参数错误......try...catch 确保这些情况不会导致程序崩溃,而是输出可读的错误信息。
7.5 运行与调试
bash
bun run index.ts
如果一切正常,终端会输出 DeepSeek 模型对 "介绍一下bun" 的回答。如果出错,catch 块会打印错误原因,帮助你排查。
8. 总结
这篇文章从零开始,串起了 Bun 后端开发的一条完整链路:
- Bun 作为下一代 JS/TS 运行时,凭借速度和开箱即用特性,正在成为 AI 工具链的基础设施。
- TypeScript 的类型系统在编译阶段拦截类型错误,从根本上消灭了 JS 弱类型带来的静默 bug。
- 类型转换 (
Number()、parseInt()、+)各有适用场景,理解它们的差异能写出更健壮的代码。 - DOM API 的类型陷阱提醒我们:外部输入永远不可信,必须做显式转换。
- Promise + setTimeout 封装异步等待,是调用 LLM API 的基础工具。
- POST vs GET 的选择关乎安全性和数据容量,API 调用场景中 POST 是正确答案。
- axios 封装省去了 fetch 的样板代码,自动处理 JSON 和错误,大幅提升开发效率。
- 环境变量管理是安全底线,API Key 绝不能出现在代码仓库里。
这些概念单独看都不复杂,但组合起来就是一条完整的后端开发工作流------从理解运行时、编写类型安全的代码,到发送 HTTP 请求调用大模型接口,每一步都不可或缺。
感谢阅读
如果这篇文章对你理解 Bun 和 TypeScript 后端开发有帮助,欢迎点赞、收藏、关注。我会持续记录和分享后端学习路上的实战笔记,一起进步!