Instant 2025 年 2 月更新全解读:Explorer 升级、级联删除、`$files` 存储与 InstaQL `fields` 子句实战
2026/9/24 15:11:38 网站建设 项目流程
  • 后端
  • 数据库

【免费下载链接】instant

Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.

项目地址:https://gitcode.com/gh_mirrors/inst/instant
点击查看免费下载

Instant 是一套面向 AI 编码应用的实时后端,提供认证、权限、存储、在线状态(presence)与数据流(streams)能力。本文以 Instant 官方 2025 年 2 月发布的 "Instant News" 更新通讯为主线,逐项拆解该月发布的重点功能——Explorer 数据浏览器的查询与建链升级、onDelete: "cascade"级联删除、面向所有应用的$files文件存储命名空间、InstaQL 的fields字段裁剪子句,以及 devtool、CLI 等周边工具的配置化改进,并结合当前仓库中的官方文档与源码实现给出可直接落地的配置示例与调用代码。读完本文,你将掌握这些新特性的完整用法与底层原理,能够直接在你的 Instant 应用中上手实践。

Explorer 数据浏览器迎来一轮功能升级

2025 年 2 月更新中,Instant 的 Explorer(数据浏览器)获得了一轮集中升级,主要围绕三个痛点:查找与过滤数据、在可视化界面中创建实体链接、以及浏览器状态的可分享性。

字段级筛选:字符串、比较操作符与精确匹配

此前在 Explorer 中筛选数据的能力有限,本次更新后,你可以:

  • 选择字段并对字符串进行查询:直接选中某个属性字段,输入字符串进行模糊匹配,快速定位符合条件的行;
  • 使用比较操作符:对数值、日期等类型的字段应用$gt$lt$gte$lte等比较运算符,构造范围查询;
  • 设置精确匹配:例如针对 ID 这类唯一标识,直接做等值匹配,精确锁定某一条记录。

这对应了 Explorer 前端中查询输入与行编辑的能力。在仓库中,Explorer 组件位于 client/packages/components/src/components/explorer,其中 search-input.tsx 承担查询输入交互,edit-row-dialog.tsx 承担行级编辑弹窗。在底层查询层面,比较操作符与精确匹配由 InstaQL 的where子句驱动,完整的查询语法说明见 InstaQL 官方文档。

在界面中为实体添加链接(Links)

Explorer 的第二个重要变化是为实体之间添加链接提供了可视化 UI。在此之前,如果你要建立两个命名空间(namespace)之间的关联,要么进入 Sandbox 沙箱环境操作,要么编写一段事务脚本来完成,门槛较高。现在可以直接在 Explorer 界面中操作,例如把一条posts记录关联到某个profiles作者,或者把一个$files文件挂到某个profiles之下。

这一能力背后对应的是 Instant 的 Links 数据模型——它把posts.authorprofiles.authoredPosts这类双向关联定义在 schema 中,链接本身既可以由 Explorer 可视化创建,也可以在代码中通过事务(transact)创建。数据建模与 Links 的完整说明见 Modeling data 文档。

深链接(Deep-linking):可分享、可回溯的 Explorer 状态

本次更新还为 Explorer 加入了深链接支持

  • 浏览器前进/后退按钮现在可以在不同的 Explorer 视图状态之间导航;
  • 你可以直接把当前 Explorer 状态对应的 URL 发送给团队伙伴,对方打开即可直达同一筛选、同一命名空间的视图。

这让 Explorer 从"一次性工具"变成了可协作、可复现的调试入口,尤其适合团队间分享问题复现场景。

Cascade Delete:一条事务完成级联删除

当业务对象之间存在依赖关系时(例如删除一篇post时,其下所有comments也应一并删除),级联删除就非常有用。2025 年 2 月更新中,Instant 在 Explorer 中为这一能力提供了一键配置入口,你不需要再写代码,在 Explorer 的链接配置界面中即可开启。

配置方式:onDelete: "cascade"

在 schema 中,级联删除通过链接定义中的onDelete: "cascade"来声明。它只适用于has: "one"方向上的链接:当"一"侧实体被删除时,所有通过该链接关联的"多"侧实体也会被自动删除。以官方 Modeling data 文档 中的微博客示例为例:

postAuthor: { forward: { on: "posts", has: "one", label: "author", onDelete: "cascade" }, reverse: { on: "profiles", has: "many", label: "authoredPosts" }, } // 这条事务会同时删除 profile 以及所有关联的 posts db.tx.profiles[user_id].delete();

如果不加onDelete: "cascade",删除一个profile只会解除链接关系,而不会删除底层的posts数据:

postAuthor: { forward: { on: "posts", has: "one", label: "author" }, reverse: { on: "profiles", has: "many", label: "authoredPosts" }, }

链接方向也可以反过来建模,只要在has: "one"那一侧声明级联即可:

postAuthor: { forward: { on: "profiles", has: "many", label: "authoredPosts" }, reverse: { on: "posts", has: "one", label: "author", onDelete: "cascade" }, }

性能设计:级联在单次调用内完成

官方更新通讯特别强调,该功能在设计时考虑了性能:所有 on-delete 级联删除在底层都通过一次调用完成,而不是逐条递归发起删除请求。这意味着即使一个实体的关联子树规模较大,删除操作也能以单次事务的语义原子地完成,避免了多次网络往返和中间态暴露。从源码结构看,这一设计在服务端事务执行层落地:删除请求携带级联信息后,由服务端一次性解析出全部受影响实体并统一删除。

Storage 面向所有应用开放:$files系统命名空间

2025 年 2 月最重要的更新之一是存储能力对所有应用开放。每个应用现在都会在 Explorer 中看到一个全新的$files系统命名空间,它与既有的$users命名空间一样,属于 Instant 的特殊系统命名空间,专门用于上传和托管应用资源(图片、视频、文档等任意文件类型)。

一行代码上传文件

客户端上传文件非常简单,只需一行调用:

db.storage.uploadFile("hello-world", file);

上传完成后,通过查询$files命名空间即可拿到文件的下载 URL。$files与其他任何表的行为一致:你可以把文件链接到其他命名空间、进行过滤、排序,甚至为文件设置CASCADE DELETE——例如当某个profiles被删除时,其关联的全部资产文件也会被自动清理。

配额方面,免费应用可存储最多 1GB,付费应用可存储最多 10GB,超出部分按 $0.125/GB 计费(该数据来自官方通讯原文)。

Storage 快速上手:完整可运行示例

仓库中的 Storage 官方文档 提供了从零搭建图片上传应用的完整示例,步骤如下。

第 1 步:创建项目并安装 SDK

npx create-next-app instant-storage --tailwind --yes cd instant-storage npm i @instantdb/react

第 2 步:初始化 schema 与权限文件

npx instant-cli@latest init

打开instant.schema.ts,声明$files实体:

import { i } from "@instantdb/react"; const _schema = i.schema({ entities: { $files: i.entity({ path: i.string().unique().indexed(), url: i.string(), }), $users: i.entity({ email: i.string().unique().indexed(), }), }, links: {}, rooms: {}, }); // 便于 TypeScript 提供更友好的智能提示 type _AppSchema = typeof _schema; interface AppSchema extends _AppSchema {} const schema: AppSchema = _schema; export type { AppSchema }; export default schema;

打开instant.perms.ts,配置$files的权限(示例放开全部权限,便于上手,生产环境不推荐):

import type { InstantRules } from "@instantdb/react"; const rules = { "$files": { "allow": { "view": "true", "create": "true", "delete": "true" } } } satisfies InstantRules; export default rules;

推送 schema 与权限到云端:

npx instant-cli@latest push

第 3 步:编写上传与展示代码

'use client'; import { init, InstaQLEntity } from '@instantdb/react'; import schema, { AppSchema } from '../instant.schema'; type InstantFile = InstaQLEntity<AppSchema, '$files'> const APP_ID = process.env.NEXT_PUBLIC_INSTANT_APP_ID; const db = init({ appId: APP_ID, schema }); // uploadFile 是实际执行上传的 API; // 上传完成后 $files 查询会自动更新 async function uploadImage(file: File) { try { // 可选的上传元数据 const opts = { // 对应 HTTP Content-Type 头,默认 'application/octet-stream' contentType: file.type, // 对应 HTTP Content-Disposition 头,默认 'inline' contentDisposition: 'attachment', }; await db.storage.uploadFile(file.name, file, opts); } catch (error) { console.error('Error uploading image:', error); } } // $files 是查询存储数据的专用命名空间 function App() { const { isLoading, error, data } = db.useQuery({ $files: { $: { order: { serverCreatedAt: 'asc' }, }, }, }); // $files 查询结果包含元数据与可用于提供文件的下载 URL const { $files: images } = data; // ... 渲染图片网格,使用 image.url 作为 <img> 的 src } // 使用 db.transact 删除文件 const handleDelete = async (image: InstantFile) => { db.transact(db.tx.$files[image.id].delete()); }

启动应用即可看到实时刷新的图片流:

npm run dev

存储客户端 SDK 的完整 API

Storage 客户端 SDK 围绕db.storage提供了一套完整 API(详见 Storage 官方文档)。

上传(Upload)

db.storage.uploadFile(path, file, opts?)三个参数的作用:

  • path:文件在存储中的存放路径,可用于权限规则限制对特定路径的访问;
  • fileFile类型对象,通常来自<input type="file">
  • opts:可选的元数据,如contentTypecontentDisposition
// 以文件名作为路径 await db.storage.uploadFile(file.name, file); // 自定义路径(例如按用户 ID 分目录) const path = `${user.id}/avatar.png`; await db.storage.uploadFile(path, file); // 设置 content-type 与 content-disposition const path = `${user.id}/orders/${orderId}.pdf`; await db.storage.uploadFile(path, file, { contentType: 'application/pdf', contentDisposition: `attachment; filename="${orderId}-confirmation.pdf"`, });

覆盖(Overwrite):如果path已存在,再次上传会直接覆盖原文件;若不想覆盖,需要保证每个文件的路径唯一。

查看(View):查询$files命名空间即可获取文件列表,返回的对象包含idpath、可直接用于提供文件的urlcontent-typecontent-disposition等字段。也可以像查询普通命名空间一样,用过滤器与关联对文件进行过滤和排序:

const query = { $files: { $: { order: { serverCreatedAt: 'asc' }, }, }, }; const { isLoading, error, data } = db.useQuery(query);

删除(Delete):通过db.transact删除文件,支持按 id、按 path(使用lookup)、批量删除:

// 按 id 删除 db.transact(db.tx.$files[fileId].delete()); // 按 path 删除 db.transact(db.tx.$files[lookup('path', 'photos/demo.png')].delete()); // 批量删除 db.transact(fileIds.map((id) => db.tx.$files[id].delete()));

更新(Update):可以用db.transact更新文件的path以及自定义列。由于path是唯一属性,若目标 path 已存在,事务会失败。目前仅允许更新$filespath属性和自定义列,更新content-type等内置属性会导致事务失败。

const { data } = await db.query({ $files: { $: { where: { path: { $like: 'documents/my-video-project/%' } } } }, }); await db.transact( data.$files.map((file) => db.tx.$files[file.id].update({ path: file.path.replace( 'documents/my-video-project/', 'videos/my-video-project/', ), isFavorite: true, }), ), );

链接(Link):上传成功后,uploadFile返回包含文件 ID 的data对象,可以用它把文件链接到其他命名空间:

async function uploadImage(file: File) { const path = `${user.id}/avatar`; const { data } = await db.storage.uploadFile(path, file); await db.transact(db.tx.profiles[profileId].link({ avatar: data.id })); }

存储上传的底层实现

db.storage.uploadFile在客户端最终会向服务端发起一次PUT 请求到/storage/upload端点,把文件二进制作为请求体,并在请求头中携带app-idpathauthorization(Bearer token)、content-type与可选的content-disposition。对应的实现位于 StorageAPI.ts:

export async function uploadFile({ apiURI, appId, path, file, refreshToken, contentType, contentDisposition }) { const headers = { 'app-id': appId, app_id: appId, path, authorization: `Bearer ${refreshToken}`, 'content-type': contentType || file.type, }; if (contentDisposition) { headers['content-disposition'] = contentDisposition; } const data = await jsonFetch(`${apiURI}/storage/upload`, { method: 'PUT', headers, body: file, }); return data; }

删除文件则对应DELETE /storage/files?app_id=...&filename=...端点(见 StorageAPI.ts)。从源码结构看,旧版基于预签名 URL 的getSignedUploadUrl/getDownloadUrlAPI 已在代码中标记为Deprecated(2025 年 1 月起弃用),新应用应直接使用db.storage.uploadFile$files命名空间。

存储权限模型

Storage 权限默认是禁用的:在你显式设置权限之前,任何上传和下载都不会被允许。权限与$files命名空间的操作一一对应:

  • create权限启用上传$files
  • view权限启用查看$files
  • update权限启用更新$files
  • delete权限启用删除$files
  • $filesview权限加上对正向实体的update权限,可以启用文件的链接/取消链接操作。

权限规则中可以使用auth访问当前认证用户,使用data访问文件元数据。目前唯一可用的文件元数据是data.path(文件在存储中的路径)。几个典型示例:

允许任何人上传与查看(易上手,不建议生产使用):

{ "$files": { "allow": { "view": "true", "create": "true" } } }

仅允许已登录用户查看与上传:

{ "$files": { "allow": { "view": "isLoggedIn", "create": "isLoggedIn" }, "bind": ["isLoggedIn", "auth.id != null"] } }

已登录用户只能上传、查看、更新自己子目录下的文件:

{ "$files": { "allow": { "view": "isOwner", "update": "isOwner", "create": "isOwner" }, "bind": ["isOwner", "data.path.startsWith(auth.id + '/')"] } }

React Native 与 Admin SDK 下的存储使用

  • React NativeuploadFile期望FileBlob。Expo SDK 56 及以上版本中expo/fetch是全局 fetch 且可直接读取本地文件,可从expo-file-system传入File对象;Expo SDK 55 及以下(或裸 React Native)则先用fetch读取本地文件再包装成File。详见 Storage 官方文档。
  • Admin SDK:服务端同样提供db.storage.uploadFile(path, file, opts?),此时file必须是 buffer 或 stream;以 stream 上传时必须额外提供fileSize选项。Admin SDK 不强制校验权限,因此可以在无认证环境下管理文件,查询使用db.query()而非db.useQuery()

InstaQLfields子句:按需裁剪查询字段

默认情况下,InstaQL 查询会返回对象的所有字段。2025 年 2 月更新新增了fields子句,允许你只取需要的字段子集或"桩数据"(stub),官方文档见 InstaQL 文档的 Select fields 小节。

基础用法

const query = { goals: { $: { fields: ['status'], }, }, }; const { isLoading, error, data } = db.useQuery(query);

返回结果中只会包含id与指定的status字段(注意id始终会被返回,即使未显式声明):

{ "goals": [ { "id": standupId, "status": "in-progress" }, { "id": standId, "status": "completed" } ] }

嵌套关联中使用

fields同样适用于嵌套关联——每个层级的$块都可以独立指定fields

const query = { goals: { $: { fields: ['title'], }, todos: { $: { fields: ['id'], }, }, }, }; const { isLoading, error, data } = db.useQuery(query);

返回的每个goal只带title与其下仅含idtodos列表。这在查询深层关联结构时尤其有用,可以显著压缩查询载荷。

性能收益

官方文档明确指出,使用fields有两个层面的性能收益:

  1. 减少传输数据量:服务端只需要返回被选中的字段,降低了网络带宽消耗;
  2. 减少 React 重渲染次数:当查询结果中未选中的字段发生变化时,不会触发组件重新渲染——只要所选字段没有变化,前端就不会发生不必要的 re-render。

因此在构建列表页、卡片墙这类"只需要展示字段子集"的场景时,fields是一个低成本、高收益的查询优化手段。

其余周边更新一览

2025 年 2 月还有几项值得关注的周边改进:

React Native Web 改用 IndexedDB

@instantdb/react-native现在在react-native-web环境中使用IndexedDB作为存储后端,与 Web 端的 react 包行为保持一致。这意味着同一个应用在 Web 与 React Native Web 之间的数据缓存与同步行为将更加统一,降低了跨端调试的心智负担。

Rooms 使用静态 hooks 并返回稳定值

Rooms(在线状态/presence 相关)被更新为使用静态 hooks并返回稳定值(stable values)。这消除了使用 presence 时产生的类型告警,同时提升了性能——稳定的返回值意味着 React 组件不会因引用变化而频繁重渲染。

Devtool 更可配置

Instant 的开发工具条(devtool)现在支持更多配置项。在 coreTypes.ts 中,DevtoolConfig定义了三个可配置项:

配置项含义默认值
positiondevtool 面板在屏幕上的位置'bottom-right'
allowedHosts允许展示 devtool 的主机列表['localhost']
dashURI用于渲染 devtool 的 Dashboard URI'https://instantdb.com'

position支持'bottom-left''bottom-right''top-right''top-left'四种取值。官方通讯中提到:Vercel 的开发工具默认放在左下角,所以你可以把 Instant 的 devtool 移到右下角以避免重叠;同时,如果你在 localhost 之外的开发环境运行应用,可以配置allowedHosts让 devtool 仍然正常展示。

对应的渲染实现位于 devtool.ts,init的配置入口在 core/src/index.ts,devtool 通过 iframe 方式挂载,元素类名包括instant-devtool-iframeinstant-devtool-togglerinstant-devtool-container

CLI 支持从自定义路径加载 schema 与 perms

CLI 现在可以从自定义路径加载instant.schema.tsinstant.perms.ts。从源码 findConfigCandidates.ts 看,实现机制如下:

  • 支持通过环境变量INSTANT_SCHEMA_FILE_PATHINSTANT_PERMS_FILE_PATH显式指定配置文件路径(写入时同样会优先使用这两个环境变量,见getSchemaPathToWrite/getPermsPathToWrite);
  • 未设置环境变量时,CLI 会按优先级自动查找:项目根目录的instant.schema/instant.permssrc目录下最多 3 层深度的同名文件,lib目录下最多 2 层深度的同名文件,以及app/instant.schema/app/instant.perms
  • 每种配置都支持tsmtsctsjsmjscjs多种扩展名;
  • 查找机制同样适用于 email 模板文件instant.email(支持INSTANT_EMAIL_FILE_PATH环境变量)。

另外值得注意的是,schema 文件中的框架包导入会被重写:由于@instantdb/react-native@instantdb/svelte@instantdb/vue等包无法在 Node.js CLI 上下文中加载(它们会引入 react-native、.svelte.vue文件),CLI 会将这些导入重写为对应包的dist/cli入口,该入口仅从@instantdb/core重新导出 schema 所需的类型(iidtx等),从而保证 CLI 在任何框架项目中都能顺利解析 schema。

技术文章:零停机迁移 Postgres 16

对于喜欢技术内容的读者,官方同步发布了一篇关于零停机迁移到 Postgres 16的文章,该文章与本文档同处仓库中:pg_upgrade.md。文章探讨了 Instant 后端在保持服务可用性的同时完成 Postgres 大版本升级的方案,对数据库运维与迁移设计感兴趣的读者可以继续深入。

小结

2025 年 2 月的这轮更新让 Instant 的日常开发体验上了一个台阶:Explorer 从"只读工具"进化为可筛选、可建链、可分享的可视化开发台;级联删除让数据清理变得声明式且高性能;$files存储命名空间把文件上传、托管、查询、链接与权限统一进了既有的数据模型;fields子句则为查询瘦身提供了官方原生的手段。如果你正在使用 Instant 构建应用,可以对照本文的示例,从 Explorer 的深链接分享、级联删除配置、$files图片上传与fields查询优化这四个切入点开始实践,并结合 Storage 官方文档、Modeling data 文档 与 InstaQL 文档 进一步挖掘能力。

  • 后端
  • 数据库

【免费下载链接】instant

Instant is the best backend for AI-coded apps. You get auth, permissions, storage, presence, and streams — everything you need to ship apps your users will love.

项目地址:https://gitcode.com/gh_mirrors/inst/instant
点击查看免费下载
上一篇:CANN opbase 算子日志与校验宏全解析:OP_LOGE 系列与错误码上报机制实战指南
下一篇:dotnet/skills 评测缺陷诊断目录实战:用「症状 → 原因 → 修复」体系为技能评估排障

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询