资讯动态

深入 Puter Key-Value Store API:在用户云端存储中读写键值数据的完整实战指南

发布时间:2026/9/10 14:13:13 来源:尧图企业网站定制
深入 Puter Key-Value Store API在用户云端存储中读写键值数据的完整实战指南【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter导读Puter 的 Key-Value StoreKVAPI 让开发者无需搭建服务器、无需关心扩容与备份即可在用户自己的云存储账户中使用键值对存取数据。本文围绕 KV.md 官方文档展开系统讲解puter.kv模块的全部 12 个方法与容量上限常量set/get/del/incr/decr/add/remove/update/expire/expireAt/list/flush并逐条结合仓库中 puter-js 模块源码 验证参数边界、存储隔离与返回语义。读完你可以在网站、应用、Node.js 与 Serverless Worker 中直接落地 KV 存储学会用键前缀完成查询式过滤、用 cursor 分页遍历大数据集、用 TTL 做缓存过期以及如何安全地设计键布局与跨用户共享方案。什么是 Puter KV数据存在用户的云盘里Key-Value Store API 允许应用通过简单的键值对在云端保存和读取数据。它支持set、get、delete、list keys、incr/decr、flush等一系列操作足以支撑应用数据持久化、缓存、配置项存储等常见需求。所有基础设施服务器、扩容、备份由 Puter 托管借助 User-Pays Model读写与存储成本由应用的用户自己承担开发者无需担心按量付费账单。从源码看该能力在客户端 SDK 中由 KVModule 类 暴露为puter.kv方法实现分布在src/puter-js/src/modules/kv/目录下set.js、get.js、list.js等均基于makeDriverMethod转发到puter-kvstore驱动接口官方同时提供了相应的单元测试。存储边界每个 App 在每个用户账户内都有自己的库需要反复强调的隔离规则是每个用户的 KV store 归属于其个人账户一个用户无法读取另一个用户的数据在同一个用户账户内每个 App 又各自拥有独立命名空间默认情况下应用之间互不可见。这意味着 KV 非常适合存放用户专属的私密状态。要构建所有用户共享的集中式存储官方推荐使用 Serverless WorkerWorker 代码可代表 Worker 拥有者的资源执行因此所有用户共用同一份后端存储从而实现跨用户读写同一份数据。键布局就是访问边界想让他人监视你 store 的一部分而不是复制整份数据时可以通过 Events 共享句柄 对某个键前缀发起授权share handle。句柄会固定住其被授权时的前缀因此之后重组键会破坏所有已发放的句柄——建议在稳定的合成分段上授权例如workspace:uuid:而不要用容易被改名的语义化命名如q3-planning:。全部可用函数一览以下函数在 Puter.js 中开箱即用完整签名与详解分别对应src/docs/src/KV/下的同名子文档函数作用子文档puter.kv.set()写入/更新一个键值对set.mdputer.kv.get()按键读取值get.mdputer.kv.incr()数值自增incr.mdputer.kv.decr()数值自减decr.mdputer.kv.add()向已有键追加值add.mdputer.kv.remove()按路径删除值remove.mdputer.kv.update()按路径更新值update.mdputer.kv.del()删除键值对del.mdputer.kv.expire()设置秒级 TTL 过期expire.mdputer.kv.expireAt()设置过期时间戳expireAt.mdputer.kv.list()列出键/键值支持前缀匹配与分页list.mdputer.kv.flush()清空当前应用的全部数据flush.md从 KVModule 源码 可看到这些方法在类中逐一声明并在构造函数中完成bind因此解构调用如const { get } puter.kv依然可用puter.kv.clear还是flush的别名puter.kv.clear puter.kv.flush。写数据puter.kv.set()当传入 key 和 value 时set会把键值加入用户 store若 key 已存在则更新其值。语法puter.kv.set(key, value) puter.kv.set(key, value, expireAt) puter.kv.set({ key, value, expireAt }) puter.kv.set([ { key, value, expireAt }, ... ]) puter.kv.set({ items: [ { key, value, expireAt }, ... ] })从实现来看set.js 会根据首个参数的类型自动分派数组走batchPut批量路径对象且非数组时走单键路径第二、三个位置还兼容(key, value, expireAt)的可选第三参数并支持尾随 legacy 的 success/error 回调。参数keyString必填要创建/更新的键名最大1 KB超出由assertKeySize在本地前置校验时抛错。valueString | Number | Boolean | Object | Array要赋予的值。对象与数组原样存取、原样返回最大400 KB。数值精度与 JavaScript 自身一致值中任意数字含嵌套在对象/数组内部的必须处于±9,007,199,254,740,991即Number.MAX_SAFE_INTEGER范围内超出会被钳制到边界而非拒绝NaN会被存为null。若 ID 或累计总量必须精确超过该范围请以字符串形式存储。expireAtNumber可选键过期的 Unix 时间戳秒。disableSharingBoolean可选放在尾部 options 对象中传入set(key, value, { disableSharing: true })将该条目标记为仅本 App 私有。私有条目即使用户已通过puter.perms.request(appData, …)授权了 App 数据其他 App 也无法读取、列出、修改或删除——适合存放任何其他 App 都不该看到的缓存访问令牌。批量形式同样支持set([...items], { disableSharing: true })会把整批条目标记为私有。你自己的 App 正常读写不受影响若再次写入同一 key 时不再携带该标记条目将重新变为可共享因为set会整体替换该条目。itemsArray仅批量由{ key, value, expireAt? }组成的数组单次请求写入多个条目。每个 key 必填并遵循相同的 1 KB/400 KB 限制数组可直接传set([...])也可用对象包裹set({ items: [...] })。注意 set.js 的批量分支 会逐个校验并报错如items_required、invalid_item、key_undefined。返回值Promise创建或更新成功后 resolve 为true。示例写入其他 App 永远读不到的值html body script srchttps://js.puter.com/v2//script script puter.kv.set(accessToken, secret-value, { disableSharing: true }) .then(() puter.print(Stored privately)); /script /body /html示例批量写入html body script srchttps://js.puter.com/v2//script script (async () { await puter.kv.set([ { key: name, value: Puter Smith }, { key: age, value: 21 }, ]); puter.print(Batch set complete); })(); /script /body /html读数据puter.kv.get()语法puter.kv.get(key)参数与返回值keyString必填要读取的键名。返回Promiseresolve 为该键的值键不存在时 resolve 为undefined。示例html body script srchttps://js.puter.com/v2//script script (async () { // (1) 创建键值对 await puter.kv.set(name, Puter Smith); puter.print(Key-value pair name created/updatedbr); // (2) 读取键 name const name await puter.kv.get(name); puter.print(Name is: ${name}); })(); /script /body /html数值原子操作incr/decr计数器常用于点赞数、浏览量、库存等场景且为服务端原子操作天然避免并发覆盖问题。puter.kv.incr()对键的值自增。键不存在时会先初始化为 0 再执行操作若键值是错误类型或无法表示为整数的字符串会返回错误。该操作限定在64 位有符号整数范围内。puter.kv.incr(key) puter.kv.incr(key, amount) // amount 默认 1 puter.kv.incr(key, pathAndAmount) // 对象形式自增对象值内的属性路径当amount传对象时是对存储在键中的对象值的某个属性做自增对象的 key 为点分路径如user.scorevalue 为自增量。amount必须在±9,007,199,254,740,991内超过会被钳制后应用计数器的总量要保持精确也须落在同一范围内需要越过该值累加的请用字符串配合puter.kv.set()自管。html body script srchttps://js.puter.com/v2//script script puter.kv.incr(testIncrKey).then((newValue) { puter.print(New value: ${newValue}); }); /script /body /html嵌套路径自增html body script srchttps://js.puter.com/v2//script script (async () { // 假设 stats 中存有: { user: { score: 10 } } await puter.kv.set(stats, {user: {score: 10}}) // 将 user.score 自增 2 const newValue await puter.kv.incr(stats, {user.score: 2}); // newValue 为: { user: { score: 12 } } puter.print(New value: ${JSON.stringify(newValue)}); })(); /script /body /htmlputer.kv.decr()语义与incr完全对称默认自减 1、键不存在先初始化为 0、限定 64 位有符号整数同样支持对象形式对嵌套属性自减{user.score: 2}会把 10 减成 8。详细参考 decr.md。追加与按路径修改add/update/remove这三个方法让你在不读改写整个值的前提下精细地操作对象/数组内部的字段。puter.kv.add()向数组/路径追加值传数组时逐元素追加到键存储的数组传对象时每个 key 视为点分路径把值加到该路径。puter.kv.add(key, value) puter.kv.add(key, pathAndValue)value缺省时为1追加数组会逐元素展开因此想追加单个数组元素需用puter.kv.add(scores, [5])会把5作为一个元素追加。追加的值遵循与set相同的限制400 KB所有数字须在±9,007,199,254,740,991内超出按钳制存储。返回Promiseresolve 为键更新后的完整值。html body script srchttps://js.puter.com/v2//script script (async () { await puter.kv.set(profile, { tags: [alpha] }); const updated await puter.kv.add(profile, { tags: [beta, gamma] }); puter.print(Updated profile: ${JSON.stringify(updated)}); })(); /script /body /htmlputer.kv.update()按路径改值并可刷新 TTL在不覆盖整个值的前提下更新嵌套字段puter.kv.update(key, pathAndValueMap) puter.kv.update(key, pathAndValueMap, ttl) puter.kv.update({ key, pathAndValueMap, ttl })pathAndValueMapObject必填键为点分路径如profile.name值为该路径的新值限制同set400 KB / ±MAX_SAFE_INTEGER。ttlNumber可选刷新该键的生存时间秒。返回更新后键的完整值。html body script srchttps://js.puter.com/v2//script script (async () { await puter.kv.set(profile, { name: Puter, stats: { score: 10 } }); const updated await puter.kv.update( profile, { stats.score: 11, name: Puter Smith }, 3600 ); puter.print(Updated profile: ${JSON.stringify(updated)}); })(); /script /body /htmlputer.kv.remove()按路径删除字段puter.kv.remove(key, ...paths)pathsString[]必填一个或多个点分路径如profile.bio。返回删除后的键值。html body script srchttps://js.puter.com/v2//script script (async () { await puter.kv.set(profile, { name: Puter, stats: { score: 10, level: 2 } }); const updated await puter.kv.remove(profile, stats.score); puter.print(Updated profile: ${JSON.stringify(updated)}); })(); /script /body /html删除与清空del/flushputer.kv.del(key)移除指定键若键不存在则什么都不发生成功 resolve 为true。示例可完整还原写→删→读回 undefined的全过程见 del.md。puter.kv.flush()清空当前 App 在当前用户账户下的所有键值对无参数成功 resolve 为true。多用于测试清理或重置用户数据功能。典型流程是写入几条数据 →list()确认 →flush()→ 再次list()应为空数组。示例见 flush.md。html body script srchttps://js.puter.com/v2//script script (async () { // (1) 创建若干键值对 await puter.kv.set(name, Puter Smith); await puter.kv.set(age, 21); await puter.kv.set(isCool, true); puter.print(Key-value pairs created/updatedbr); // (2) 清空整个 store await puter.kv.flush(); puter.print(Key-value store flushedbr); })(); /script /body /html键生命周期expire/expireAt为键设置过期时间非常适合会话缓存、临时验证码等到时自动消失的数据。puter.kv.expire(key, ttlSeconds)ttlSeconds为从当前起多少秒后删除该键成功 resolve 为true。puter.kv.expireAt(key, timestampSeconds)timestampSeconds为删除该键的Unix 时间戳秒。官方示例用(Date.now()/1000) 1让键在 1 秒后过期随后setTimeout等 2 秒再get应得到undefined详见 expireAt.md。html body script srchttps://js.puter.com/v2//script script (async () { // (1) 创建键值对 await puter.kv.set(name, Puter Smith); puter.print(Key-value pair name created/updatedbr); // (2) 设置 1 秒后过期 await puter.kv.expire(name, 1); // (3) 等待 2 秒后再读取 setTimeout(async () { const name await puter.kv.get(name); puter.print(Value :, name); }, 2000); })(); /script /body /html另外set(key, value, expireAt)可以在写入时直接携带过期时间戳一步完成写 定生死。枚举与查询puter.kv.list()返回当前 App 在该用户 store 中的全部键无键时返回空数组。结果按键名按字典序字符串序排序这是利用键前缀做范围查询的基础。语法puter.kv.list() puter.kv.list(pattern) puter.kv.list(returnValues false) puter.kv.list(pattern, returnValues false) puter.kv.list(options)pattern前缀匹配pattern为前缀式匹配*通配符只允许出现在末尾。例如abc与abc*都会匹配以abc开头的键abc、abc123、abc123xyz。要匹配字面量*在末尾再加一个*如key**匹配以key*开头的键k*y*能匹配k*y前缀。默认*即匹配全部键。注意方向性差异KV 的 pattern永远是前缀匹配无论末尾是否带*而 Events 主题恰好相反——kv:cart只监视这一把键要扩展为前缀需额外加*。returnValues为true时返回含key与value属性的对象数组false默认只返回键名字符串数组。options 对象分页相关pattern同 pattern 参数。returnValues同 returnValues 参数。limitNumber单次调用最多返回条数。cursorString上一页返回的分页游标传给本参数即可取下一页。offsetNumber跳过的条数后开始本页。不推荐——offset 越大请求越慢越贵优先cursor。最大5000且不能与cursor混用。includeTotalBoolean为true时结果附带匹配查询的全部条数跨所有页。该计数按量计费且随 store 规模增长成本增加——只在首页请求一次避免放在热点路径仅需判断是否还有下一页时应检查cursor而非计数。fetchUntilFullBoolean一页可能因排除过期键等原因返回少于limit的条数为true时尽量把页填满到limit条需同时给出limit。streamBoolean为true时返回KVListPage对象的异步迭代器而非 Promise配合for await ... of使用。可结合limit控制页大小或cursor从上一页续读不能与offset组合开启includeTotal时仅第一页携带total。返回值形态Promiseresolve 为以下三种之一当前 App 的全部键数组或含KVPair的键值对对象数组或使用了limit/cursor/offset/includeTotal/fetchUntilFull任一 option 时返回KVListPage对象。分页时应循环迭代直到结果中没有cursor——即使某页少于limit条仍可能有更多页。传统非分页的list()返回值仍是普通数组SDK 在底层改为逐页拉取但它依然会读遍整个 store大 store 上裸调list()会又慢又贵跨多页的全量列表SDK 会一次性输出 console 警告。建议优先stream: true或显式limit/cursor并用pattern缩小扫描范围。流式读取for await (const page of puter.kv.list({ pattern: log:*, stream: true })) { for (const key of page.items) { console.log(key); } }示例返回全部键、键值对、以及前缀匹配html body script srchttps://js.puter.com/v2//script script (async () { // (1) 创建键值对 await puter.kv.set(name, Puter Smith); await puter.kv.set(age, 21); await puter.kv.set(isCool, true); puter.print(Key-value pairs created/updatedbrbr); // (2) 取出全部键 const keys await puter.kv.list(); puter.print(Keys are: ${keys}brbr); // (3) 同时取出键和值 const key_vals await puter.kv.list(true); puter.print(Keys and values are: ${(key_vals).map((key_val) key_val.key key_val.value)}brbr); // (4) 用 pattern 匹配键 const keys_matching_pattern await puter.kv.list(is*); puter.print(Keys matching pattern are: ${keys_matching_pattern}br); // (5) 清理 await puter.kv.del(name); await puter.kv.del(age); await puter.kv.del(isCool); })(); /script /body /html示例cursor 分页html body script srchttps://js.puter.com/v2//script script (async () { // 造 6 条示例数据 for (let i 1; i 6; i) { await puter.kv.set(item-${i}, value-${i}); } puter.print(Created 6 key-value pairsbrbr); // 每页 2 条游标翻页 let currentCursor undefined; let page 1; do { const result await puter.kv.list({ limit: 2, returnValues: true, cursor: currentCursor, }); const items result.items; puter.print(bPage ${page}:/bbr); for (const item of items) { puter.print( ${item.key} ${item.value}br); } puter.print(br); currentCursor result.cursor; page; } while (currentCursor); puter.print(Done paginating.brbr); // 清理 for (let i 1; i 6; i) { await puter.kv.del(item-${i}); } puter.print(Cleaned up sample data.); })(); /script /body /html键设计即查询计划用前缀实现类 SQL 过滤由于 KV 只有精确读一个键和按前缀列出两种读取方式过滤能力来自键前缀而不是查询语言。list(log:*)会把时间戳写入键名并按字典序排序天然得到按时间排序的日志序列——这是把 ISO 时间戳放进键的原因。数值键要按数字排序则需零填充到定宽item:001、item:002、item:010、item:100若存item:1、item:2、item:10字典序会排成 1、10、2。需要多条查询路径时可以用冗余前缀为同一份订单建多个读路径官方示例把每张订单同时写入order:by-id:idorder:by-status:status:idorder:by-customer:customer:idorder:by-status-customer:status:customer:id于是list(by-status:pending:*)即查询 status pendinglist(by-status-customer:pending:alice:*)即statuspending 且 customeralice完整可运行代码见 KV/list.md 的 Design keys for query-like filtering 示例。原则是每多一种查询需求就多设计一种前缀友好的键。相应地前缀模式结合字典序还意味着——多写一个键布局并预先想好键命名是 KV 应用最重要的架构决策。容量上限常量MAX_KEY_SIZE/MAX_VALUE_SIZEputer.kv.MAX_KEY_SIZEstore 允许的最大键大小字节1 KB。puter.kv.MAX_VALUE_SIZEstore 允许的最大值大小字节400 KB。对应文档见 MAX_KEY_SIZE.md 与 MAX_VALUE_SIZE.md。从 index.js 可知这两个只读属性直接暴露了 lib/validate.js 中的常量客户端在set/批量set时会先调用assertKeySize/assertValueSize做本地预校验。html body script srchttps://js.puter.com/v2//script script puter.print(Max Key Size: puter.kv.MAX_KEY_SIZE); puter.print(Max Value Size: puter.kv.MAX_VALUE_SIZE); /script /body /html权限与跨应用数据访问每个 App 的 KV store 位于各自用户账户内另一 App仅当用户显式授予appData权限puter.perms.request(appData, …)时才能触达——而且永远无法触达你用disableSharing写入的条目。从 Puter.js 权限模块看appData属于应用数据级别的授权原语appData.js服务端据此裁决跨 App 访问。安全要点回顾隐私条目缓存令牌、密钥类材料一律加disableSharing: true用户授权请求不会暴露你的 store 内容但已共享的条目可能被其他受信 App 读取勿在其中存放绝密数据需要跨用户共享一份数据时不要试图跨用户读取应把数据放进 Serverless Worker 的后端存储需要让另一账户只监视某前缀而非复制数据时使用 Events 的 share handle并注意句柄与键前缀的绑定关系。从源码理解一次set调用以 set.js 为例一次写入的调用链大致是SDK 端解析参数形态位置参数 / 对象 / 数组 /{ items }index.js 中bind后的方法把请求交给对应实现文件setSingle/setBatch经parseTrailingArgs解析尾部可选参数expireAt、optConfig、legacy 回调并在preprocess阶段执行assertKeyPresent、assertKeySize、assertValueSize本地校验通过utils.makeDriverMethod调用名为puter-kvstore的驱动接口method: set或batchPut由 Puter 后端在用户自己的云存储中完成落盘Promise resolve 为true或返回更新后的值add/update/remove/incr/decr等会回传新值。这套本地参数校验 驱动接口 服务端执行的结构让 KV 操作在前端拿到 1 KB/400 KB 边界、MAX_SAFE_INTEGER钳制、NaN → null等一致性语义官方测试 kv.test.js 覆盖了这些公共签名与行为。典型落地场景清单应用状态持久化记住用户偏好、草稿、进度点缓存层expire/expireAtupdate刷新 TTL 实现带过期的缓存配置存储功能开关、主题、语言等按用户维度隔离计数器播放量、点赞数用incr/decr原子累加嵌套结构用add/update/remove以点分路径操作对象内部字段而不做整值覆盖可排序日志/时序数据键内嵌零填充序号或 ISO 时间戳配合前缀扫描输出有序列表多条件查询维护多条前缀索引键把过滤需求编码进键设计。所有示例均可直接在浏览器控制台引入https://js.puter.com/v2/后运行验证也可在应用端安装puter-js包后在 Node.js 与 Serverless Worker 环境使用上述子文档platforms均标注websites, apps, nodejs, workers。动手前建议先在 KV 相关示例 中挑选对应玩法跑通再进入业务集成。【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价