fetch-sdk
v2.0.0
Published
A modular Fetch API client with streaming and resumable file transfers
Maintainers
Readme
Fetch SDK 2.0
Fetch SDK 2.0 是一个零运行时依赖的 Fetch API 客户端,提供统一请求配置、拦截器、超时/取消、流式响应、上传下载进度,以及可持久化的文件断点续传能力。
安装
npm install fetch-sdk项目不会自动安装依赖、构建或启动服务。发布前可由使用者手动执行 npm run build。
快速开始
import client from 'fetch-sdk';
const api = client.create({
baseURL: 'https://api.example.com',
timeout: 15000,
headers: { Authorization: 'Bearer token' }
});
const users = await api.get('/users', { params: { page: 1, tag: ['new', 'vip'] } });
const created = await api.post('/users', { name: 'Ada' });
const updated = await api.patch('/users/1', { name: 'Grace' });
await api.delete('/users/1');默认根据 Content-Type 解析 JSON 或文本。通过 responseType 可以显式指定 json、text、blob、arrayBuffer、formData、response 或 stream。
const response = await api.get('/health', { responseType: 'response' });
const buffer = await api.get('/archive.zip', { responseType: 'arrayBuffer' });请求配置
request(config) 和 request(url, options) 都可用,client 本身也可以直接调用。
await api.request({
url: '/search',
method: 'POST',
params: { q: 'fetch' },
data: { limit: 20 },
credentials: 'include',
timeout: 5000
});对象、数组会自动 JSON 序列化;FormData、Blob、ArrayBuffer、URLSearchParams 和 ReadableStream 会原样作为请求体。使用 FormData 时不要手动设置 Content-Type,浏览器需要自行追加 boundary。
拦截器
const requestId = api.interceptors.request.use(config => {
config.headers['X-Request-Id'] = crypto.randomUUID();
return config;
});
api.interceptors.response.use(data => data, error => Promise.reject(error));
api.interceptors.request.eject(requestId);也可以使用 addRequestInterceptor 和 addResponseInterceptor。拦截器返回 undefined 时保留当前值,返回 null 或 false 时会使用返回值。
超时与取消
SDK 使用原生 AbortSignal,同时兼容 CancelToken:
const controller = new AbortController();
const task = api.get('/events', { signal: controller.signal });
controller.abort();
直接导入 CancelToken:
import client, { CancelToken, isCancel } from 'fetch-sdk';
const { token, cancel } = CancelToken.source();
api.get('/slow', { cancelToken: token }).catch(error => {
if (isCancel(error)) console.log(error.message);
});
cancel('superseded');错误统一为 FetchError,包含 code、config、response、status 和 cause。常见 code:ERR_BAD_RESPONSE、ETIMEDOUT、ERR_CANCELED、ERR_ABORTED。
流式请求
stream 返回原始 ReadableStream,适合自定义二进制或 SSE 处理;streamIterable 提供 for await...of 语法,并默认以 UTF-8 解码。
for await (const chunk of api.streamIterable('/ai/chat')) {
process.stdout.write(chunk);
}
const stream = await api.stream('/download/large');
const reader = stream.getReader();通过 decode: false 保留 Uint8Array,通过 parseChunk(chunk) 转换每个文本块。
文件传输
普通上传和下载
const form = new FormData();
form.append('avatar', file);
await api.post('/avatar', form);
await api.upload(file, '/files', { fieldName: 'file' });
const blob = await api.download('/files/report.pdf', { filename: 'report.pdf' });断点续传
上传按分片发送 X-Upload-Id、X-Chunk-Index、X-Total-Chunks 和 Content-Range 请求头;服务端应按这些信息保存分片并在所有分片完成后合并。下载使用 HTTP Range 和 Content-Range,需要服务端支持 206 Partial Content。
await api.uploadWithResume(file, '/uploads/chunk', {
chunkSize: 8 * 1024 * 1024,
onProgress: ({ uploaded, total, progress }) => console.log(uploaded, total, progress),
retry: 3
});
const blob = await api.downloadWithResume('/files/large.zip', {
filename: 'large.zip',
onProgress: ({ downloaded, total, progress }) => console.log(downloaded, total, progress)
});进度默认保存在 IndexedDB;不支持 IndexedDB 时降级为内存和 localStorage(二进制断点在页面刷新后无法由 localStorage 恢复)。可通过 transferStore 注入自定义存储实现,或通过 storageKey 隔离多个业务。
进度回调
onDownloadProgress 适用于普通响应流,参数为 { loaded, total, progress }。原生 Fetch 没有跨浏览器统一的上传进度事件;需要上传进度时建议使用 uploadWithResume 的分片 onProgress,它在每个分片成功后稳定回调。
兼容性与手动构建
- 需要支持 Fetch、AbortController、ReadableStream、Blob、FormData 的运行环境。
- 构建工具链要求 Node.js
>=20.19.0;运行时若没有原生 Fetch,可通过fetch配置注入实现。 - 构建配置位于
vite.config.mjs,可由使用者手动运行npm run build生成 CJS、ESM 和 UMD 包。 - 迁移不会自动重建已有
dist,首次使用新构建产物前请手动执行构建命令。
完整类型声明位于 src/index.d.ts。
