@ctil/gql
v1.1.6
Published
A lightweight GraphQL request client for cc ecosystem
Maintainers
Readme
@ctil/gql
轻量级 GraphQL 请求客户端,开箱即用地支持 Token 管理、设备指纹注入、请求/响应拦截、内存缓存与限流封装,适用于浏览器与 Node.js 场景。
- 核心能力:基于
graphql-request的统一请求管道 - 安全与体验:内置登录信息存取与无感刷新(refresh token)
- 稳健:内置防抖/速率限制与可选查询结果缓存
- 易用 API:
auth、query、mutation、sms、gql、oss六大模块开箱即用
安装
npm i @ctil/gql
# 或者
pnpm add @ctil/gql
# 或者
yarn add @ctil/gql可选依赖(仅浏览器端需要且可自动降级):
@fingerprintjs/fingerprintjs:更稳定的设备指纹生成
快速开始
1) 初始化客户端
import { initGraphQLClient, useInterceptor } from '@ctil/gql';
initGraphQLClient({
endpoint: 'https://your-graphql-endpoint/graphql',
// Authorization: 'your-initial-access-token',
// headers: { 'X-Custom-Header': 'value' },
});
// 可选:注册拦截器(支持多个)
useInterceptor({
onRequest: async ({ query, variables, headers }) => {
// 在这里可以统一追加 header / 修改 query / 埋点
return { query, variables, headers };
},
onResponse: async (data) => {
// 在这里可以统一解包响应结构
return data;
},
onError: async (error) => {
// 在这里可以统一处理错误(如上报、提示)
throw error;
},
});2) Query 查询
import { query } from '@ctil/gql';
// 列表查询(带可选缓存)
const list = await query.list({
operationName: 'user',
fields: ['id', 'username', { roles: { fields: ['id', 'roleCode'] } }],
where: { username: { _ilike: '%cai%' } },
orderBy: { id: desc },
limit: 10,
offset: 0,
});
// 主键查询
const byId = await query.byId({
operationName: 'user',
pk: '1000000000',
fields: ['id', 'username', 'phone'],
});
// 分页查询(getXxxPageList)
const page = await query.page({
operationName: 'user',
fields: ['id', 'username'],
page: 1,
size: 20,
});
// 聚合查询(count/sum/avg/max/min + nodes)
const agg = await query.aggregate({
operationName: 'user',
fields: ['id', 'username'],
aggregateFields: { count: true },
});3) Mutation 变更
import { mutation } from '@ctil/gql';
// 插入单条
await mutation.insertOne({
operationName: 'user',
fields: ['id', 'username'],
data: { username: 'caicai', phone: '18800000000' },
});
// 条件更新
await mutation.update({
operationName: 'user',
fields: { affected_rows: true },
_set: { nickname: 'Cai' },
where: { id: { _eq: 1000000000 } },
});
// 按主键更新
await mutation.updateByPk({
operationName: 'user',
fields: ['id', 'nickname'],
_set: { nickname: 'NewName' },
pk_columns: 1000000000,
});
// 条件删除
await mutation.delete({
operationName: 'user',
fields: ['id'],
where: { id: { _eq: 1000000000 } },
});4) Auth 鉴权
import { auth, setToken, removeToken, getLoginInfo } from '@ctil/gql';
// 登录(会自动持久化登录信息,并覆盖 Bearer Token)
const loginRes = await auth.login({
account: 'caicai',
password: 'caicai',
remember: true, // 浏览器:localStorage;false 为 sessionStorage
});
// 获取/移除登录态
const info = getLoginInfo();
removeToken(); // 或者 auth.logout() 同时清缓存
// 刷新 Token(请求前会自动无感刷新,无需手动调用)
await auth.refreshToken({ refreshToken: info!.refreshToken, remember: true });
// 登出(当前/全部设备/指定设备)
await auth.logout();
await auth.logoutAllDevices();
await auth.logoutDevice('device-id-xxx');5) 短信模块
import { sms } from '@ctil/gql';
await sms.send({ phone: '18800000000' });
await sms.verify({ phone: '18800000000', code: '123456' });6) 原生 GQL 执行
import { gql } from '@ctil/gql';
const data = await gql.execute(`
query user_by_pk($id: Long!) {
user_by_pk(id: $id) { id username }
}
`, { id: 1000000000 });7) 对象存储 执行
import { oss } from '@ctil/gql';
// 本地文件上传
const file = new File();
const rs= await oss.uploadFile({
file: file
})
// 网络资源上传
const rs= await oss.uploadFromUrl({
url: "网络url",
})
// 根据资源ID获取文件
const rs= await oss.getFilePreview("1000000043")
Token 与登录信息管理
- 浏览器端:支持
localStorage(remember=true)或sessionStorage自动持久化 - Node.js:默认写入当前工作目录的
loginInfo.json,且保存在进程内存 - 自动无感刷新:对非
refreshToken的请求,会在请求前检查 access/refresh 过期并尝试刷新;刷新失败会清除登录态并抛错 - 快捷方法:
setToken(token)/removeToken()setLoginInfo(userToken, remember?)/removeLoginInfo()/getLoginInfo()
UserToken 结构包含:userId、loginAccount、token、refreshToken、expireAt、refreshExpireAt、roles、permissions、可选 deviceId/deviceName 等。
设备信息注入
每个请求都会自动在 Header 注入:
X-Device-IdX-Device-Name
来源:
- 浏览器端:优先使用
@fingerprintjs/fingerprintjs;失败则回落到 IndexedDB + UUID;deviceName基于平台和分辨率拼装 - Node.js:使用
node-machine-id获取机器 ID,os.hostname()作为设备名
缓存与限流
- 查询缓存:
query.list/byId/page/aggregate支持内存缓存(默认开启,TTL=5min)。- 通过第二、三个参数控制:
useCache?: boolean、ttl?: number - 变更类操作(
mutation.*)会自动清空缓存,保证一致性
- 通过第二、三个参数控制:
- 限流:
rateLimit对常见操作内置默认规则,可通过rateLimitConfig自定义:- 默认:query 每秒最多 10 次;mutation 每秒最多 3 次(并带 200ms 防抖)
- 针对登录与刷新有更严格的默认规则
API 速览
- 客户端与配置:
initGraphQLClient(config)、getClient()、useInterceptor(i)setEndpoint(url)、setHeader(k,v)、setHeaders(obj)、removeHeader(k)、clearHeaders()
- 鉴权:
auth.login、auth.register、auth.logout、auth.logoutAllDevices、auth.logoutDevice、auth.refreshToken - 查询:
query.list、query.byId、query.page、query.aggregate - 变更:
mutation.insertOne、mutation.batchInsert、mutation.update、mutation.batchUpdate、mutation.updateByPk、mutation.delete、mutation.deleteById - 短信:
sms.send、sms.verify - 原生:
gql.execute(query, variables?) - 对象存储:
oss.upload、oss.uploadFromUrl、oss.getFilePreview
所有模块均具备良好的 TypeScript 类型提示与默认返回范型:<T = requestResult<any>>。
运行与构建
- 开发构建:
npm run dev(tsup --watch) - 生产构建:
npm run build - 发布前置:
prepublishOnly会自动构建
输出:
- CJS:
dist/index.cjs - ESM:
dist/index.mjs - 类型:
dist/index.d.ts
使用注意
- 在首次使用前务必调用
initGraphQLClient初始化客户端 - 如果你手动设置/移除 Token,请使用导出的
setToken/removeToken,以确保内部客户端重建 - 使用
useInterceptor可以实现统一日志、透传 Trace-Id、错误收敛等 - 若你不希望缓存,可在查询方法中传
useCache=false
许可证
MIT © CaiCai@[email protected]
