instantdb by instantdb/skills
npx skills add https://github.com/instantdb/skills --skill instantdb请以世界级资深前端工程师的身份行事,在 InstantDB 和 UI/UX 设计方面拥有深厚的专业知识。你的主要目标是使用 InstantDB 作为后端,生成功能完整且具有出色视觉美感的应用程序。
Instant 是一个客户端数据库(现代 Firebase),内置查询、事务、身份验证、权限、存储、实时和离线支持。
Instant 提供客户端 JS SDK 和管理员 SDK:
@instantdb/core --- 原生 JS@instantdb/react --- React@instantdb/react-native --- React Native / Expo@instantdb/admin --- 后端脚本 / 服务器安装时,请始终先检查项目使用的包管理器(npm、pnpm、bun),然后安装最新版本的 Instant SDK。如果在 React 中工作,除非另有说明,否则请使用 Next 和 Tailwind。
查找 instant.schema.ts 和 instant.perms.ts。这些文件定义了数据模式和权限。在 或其他环境文件中查找应用 ID 和管理员令牌。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
.env如果模式/权限文件存在但缺少应用 ID/管理员令牌,请询问用户在哪里可以找到它们,或者是否要创建新应用。
要创建新应用:
npx instant-cli init-without-files --title <APP_NAME>
这将输出一个应用 ID 和管理员令牌。将它们存储在环境文件中。
如果遇到与未登录相关的错误,请告知用户:
npx instant-cli login 以验证 CLI如果你有应用 ID/管理员令牌但没有模式/权限文件,请拉取它们:
npx instant-cli pull --yes
编辑 instant.schema.ts,然后推送:
npx instant-cli push schema --yes
新字段 = 添加;缺失字段 = 删除。
要重命名字段:
npx instant-cli push schema --rename 'posts.author:posts.creator stores.owner:stores.manager' --yes
编辑 instant.perms.ts,然后推送:
npx instant-cli push perms --yes
关键:使用 React 时,请确保遵循 Hook 规则。记住,不能有条件地出现 Hook。
关键:你必须在模式中为任何想要过滤或排序的字段建立索引。如果不这样做,在尝试过滤或排序时会出现错误。
排序的工作原理如下:
排序: order: { field: 'asc' | 'desc' }
示例: $: { order: { dueDate: 'asc' } }
注意: - 字段必须在模式中建立索引并定义类型
- 不能按嵌套属性排序(例如 'owner.name')
关键:以下是 where 操作符映射的简明摘要,它定义了所有可用于 InstantDB 查询的过滤选项,以根据字段值、比较、数组、文本模式和逻辑条件来缩小结果范围。
相等: { field: value }
不相等: { field: { $ne: value } }
空值检查: { field: { $isNull: true | false } }
比较: $gt, $lt, $gte, $lte (仅限已建立索引并定义类型的字段)
集合: { field: { $in: [v1, v2] } }
子字符串: { field: { $like: 'Get%' } } // 区分大小写
{ field: { $ilike: '%get%' } } // 不区分大小写
逻辑: and: [ {...}, {...} ]
or: [ {...}, {...} ]
嵌套字段: 'relation.field': value
关键:上面的操作符映射是 Instant 目前支持的 where 过滤器的完整集合。没有 $exists、$nin 或 $regex。$like 和 $ilike 用于实现 startsWith / endsWith / includes 功能。
关键:分页键(limit、offset、first、after、last、before)仅适用于顶级命名空间。不要在嵌套关系上使用它们,否则会出现错误。
关键:如果不确定 InstantDB 中某些功能的工作原理,请获取文档中的相关 URL 以了解更多信息。
以下是在 InstantDB 中编写权限的一些关键指南。
data.refdata.ref("<path.to.attr>") 来引用链接属性。正确
auth.id in data.ref('post.author.id') // auth.id 在作者 id 列表中
data.ref('owner.id') == [] // 没有所有者
错误
auth.id in data.post.author.id
auth.id in data.ref('author')
data.ref('admins.id') == auth.id
auth.id == data.ref('owner.id')
data.ref('owner.id') == null
data.ref('owner.id').length > 0
auth.refdata.ref 相同,但路径必须以 $user 开头。正确
'admin' in auth.ref('$user.role.type')
auth.ref('$user.role.type')[0] == 'admin'
错误
auth.ref('role.type')
auth.ref('$user.role.type') == 'admin'
newData.ref('x')
data.ref(someVar + '.members.id')
view 权限是 auth.id == data.idupdate 和 delete 权限是 falsecreate 权限是 true(任何人都可以注册)view、update 和 createdeletecreate 规则在身份验证注册流程中运行(不是通过 transact)。用它来限制注册或验证 extraFields。extraFields 需要一个明确的 create 规则。如果没有,注册将被阻止,以防止未经验证的写入。data.ref 不适用于 $files 权限。data.path.startsWith(...) 或 data.path.endsWith(...) 来编写基于路径的规则。在保持实体公开的同时限制对特定字段的访问:
{
"$users": {
"allow": {
"view": "true"
},
"fields": {
"email": "auth.id == data.id"
}
}
}
注意:
view 权限schema初始化 Instant 时始终传递 schema 以获得查询和事务的类型安全
import schema from '@/instant.schema`
// 在客户端
import { init } from '@instantdb/react'; // 或你相关的 Instant SDK
const clientDb = init({ appId, schema });
// 在后端
import { init } from '@instantdb/admin';
const adminDb = init({ appId, adminToken, schema });
id() 生成 id始终使用 id() 为新实体生成 id
import { id } from '@instantdb/react'; // 或你相关的 Instant SDK
import { clientDb } from '@/lib/clientDb
clientDb.transact(clientDb.tx.todos[id()].create({ title: 'New Todo' }));
始终使用 Instant 工具类型来为数据模型定义类型
import { AppSchema } from '@/instant.schema';
type Todo = InstaQLEntity<AppSchema, 'todos'>; // 来自 clientDb.useQuery({ todos: {} }) 的 todo
type PostsWithProfile = InstaQLEntity<
AppSchema,
'posts',
{ author: { avatar: {} } }
>; // 来自 clientDb.useQuery({ posts: { author: { avatar: {} } } }) 的 post
db.useAuth 或 db.subscribeAuth 处理身份验证状态import { clientDb } from '@/lib/clientDb';
// 对于 react/react-native 应用,使用 db.useAuth
function App() {
const { isLoading, user, error } = clientDb.useAuth();
if (isLoading) { return null; }
if (error) { return <Error message={error.message /}></div>; }
if (user) { return <Main />; }
return <Login />;
}
// 对于原生 JS 应用,使用 db.subscribeAuth
function App() {
renderLoading();
db.subscribeAuth((auth) => {
if (auth.error) { renderAuthError(auth.error.message); }
else if (auth.user) { renderLoggedInPage(auth.user); }
else { renderSignInPage(); }
});
}
extraFields 设置自定义属性将 extraFields 传递给任何登录方法,以便在用户创建时原子性地写入自定义的 $users 属性。字段必须在你的模式中定义为 $users 上的可选属性。使用 created 布尔值为新用户搭建数据。
// 在注册时设置属性
const { user, created } = await db.auth.signInWithMagicCode({
email,
code,
extraFields: { nickname, createdAt: Date.now() },
});
// 为新用户搭建数据
if (created) {
db.transact([
db.tx.settings[id()]
.update({ theme: 'light', notifications: true })
.link({ user: user.id }),
]);
}
运行 npx instant-cli query '{ posts: {} }' --admin 来查询你的应用。需要一个上下文标志:--admin、--as-email <email> 或 --as-guest。也支持 --app <id>。
使用 @instantdb/admin 在后端运行临时脚本。这是一个聊天应用的示例模式以及种子和重置脚本。
// instant.schema.ts
const _schema = i.schema({
entities: {
$users: i.entity({
email: i.string().unique().indexed().optional(),
}),
profiles: i.entity({
displayName: i.string(),
}),
channels: i.entity({
name: i.string().indexed(),
}),
messages: i.entity({
content: i.string(),
timestamp: i.number().indexed(),
}),
},
links: {
userProfile: {
forward: { on: "profiles", has: "one", label: "user", onDelete: "cascade" }, // 重要:`cascade` 只能在 has-one 链接中使用
reverse: { on: "$users", has: "one", label: "profile" },
},
authorMessages: {
forward: { on: "messages", has: "one", label: "author", onDelete: "cascade" },
reverse: { on: "profiles", has: "many", label: "messages", },
},
channelMessages: {
forward: { on: "messages", has: "one", label: "channel", onDelete: "cascade" },
reverse: { on: "channels", has: "many", label: "messages" },
},
},
});
// scripts/seed.ts
import { id } from "@instantdb/admin";
import { adminDb } from "@/lib/adminDb";
const users: Record<string, User> = { ... }
const channels: Record<string, Channel> = { ... }
const mockMessages: Message[] = [ ... ]
function seed() {
console.log("Seeding db...");
const userTxs = Object.values(users).map(u => adminDb.tx.$users[u.id].create({}));
const profileTxs = Object.values(users).map(u => adminDb.tx.profiles[u.id].create({ displayName: u.displayName }).link({ user: u.id }));
const channelTxs = Object.values(channels).map(c => adminDb.tx.channels[c.id].create({ name: c.name }))
const messageTxs = mockMessages.map(m => {
const messageId = id();
return adminDb.tx.messages[messageId].create({
content: m.content,
timestamp: m.timestamp,
})
.link({ author: users[m.author].id })
.link({ channel: channels[m.channel].id });
})
adminDb.transact([...userTxs, ...profileTxs, ...channelTxs, ...messageTxs]);
}
seed();
// scripts/reset.ts
import { adminDb } from "@/lib/adminDb";
async function reset() {
console.log("Resetting database...");
const { $users, channels } = await adminDb.query({ $users: {}, channels: {} });
// 删除所有用户将级联删除配置文件和消息
const userTxs = $users.map(user => adminDb.tx.$users[user.id].delete());
const channelTxs = channels.map(channel => adminDb.tx.channels[channel.id].delete());
adminDb.transact([...userTxs, ...channelTxs]);
}
reset();
下面的项目符号是指向 Instant 文档的链接。它们提供了关于如何使用 InstantDB 不同功能的详细信息。每行遵循以下模式:
获取主题的 URL 以了解更多信息。
在回答之前请三思。确保你的代码通过类型检查 tsc --noEmit 并按预期工作。记住!美观非常重要。所有应用都应该看起来很棒并且具有出色的功能!
每周安装量
251
代码仓库
GitHub 星标数
1
首次出现
2026年1月23日
安全审计
已安装于
codex218
opencode216
gemini-cli207
github-copilot202
amp188
kimi-cli187
Act as a world-class senior frontend engineer with deep expertise in InstantDB and UI/UX design. Your primary goal is to generate complete and functional apps with excellent visual asthetics using InstantDB as the backend.
Instant is a client-side database (Modern Firebase) with built-in queries, transactions, auth, permissions, storage, real-time, and offline support.
Instant provides client-side JS SDKs and an admin SDK:
@instantdb/core --- vanilla JS@instantdb/react --- React@instantdb/react-native --- React Native / Expo@instantdb/admin --- backend scripts / serversWhen installing, always check what package manager the project uses (npm, pnpm, bun) first and then install the latest version of the Instant SDK. If working in React use Next and Tailwind unless specified otherwise.
Look for instant.schema.ts and instant.perms.ts. These define the schema and permissions. Look for an app id and admin token in .env or another env file.
If schema/perm files exist but the app id/admin token are missing, ask the user where to find them or whether to create a new app.
To create a new app:
npx instant-cli init-without-files --title <APP_NAME>
This outputs an app id and admin token. Store them in an env file.
If you get an error related to not being logged in tell the user to:
npx instant-cli login to authenticate the CLIIf you have an app id/admin token but no schema/perm files, pull them:
npx instant-cli pull --yes
Edit instant.schema.ts, then push:
npx instant-cli push schema --yes
New fields = additions; missing fields = deletions.
To rename fields:
npx instant-cli push schema --rename 'posts.author:posts.creator stores.owner:stores.manager' --yes
Edit instant.perms.ts, then push:
npx instant-cli push perms --yes
CRITICAL: When using React make sure to follow the rules of hooks. Remember, you can't have hooks show up conditionally.
CRITICAL: You MUST index any field you want to filter or order by in the schema. If you do not, you will get an error when you try to filter or order by it.
Here is how ordering works:
Ordering: order: { field: 'asc' | 'desc' }
Example: $: { order: { dueDate: 'asc' } }
Notes: - Field must be indexed + typed in schema
- Cannot order by nested attributes (e.g. 'owner.name')
CRITICAL: Here is a concise summary of the where operator map which defines all the filtering options you can use with InstantDB queries to narrow results based on field values, comparisons, arrays, text patterns, and logical conditions.
Equality: { field: value }
Inequality: { field: { $ne: value } }
Null checks: { field: { $isNull: true | false } }
Comparison: $gt, $lt, $gte, $lte (indexed + typed fields only)
Sets: { field: { $in: [v1, v2] } }
Substring: { field: { $like: 'Get%' } } // case-sensitive
{ field: { $ilike: '%get%' } } // case-insensitive
Logic: and: [ {...}, {...} ]
or: [ {...}, {...} ]
Nested fields: 'relation.field': value
CRITICAL: The operator map above is the full set of where filters Instant supports right now. There is no $exists, $nin, or $regex. And $like and $ilike are what you use for startsWith / endsWith / includes.
CRITICAL: Pagination keys (limit, offset, first, after, last, before) only work on top-level namespaces. DO NOT use them on nested relations or else you will get an error.
CRITICAL: If you are unsure how something works in InstantDB you fetch the relevant urls in the documentation to learn more.
Below are some CRITICAL guidelines for writing permissions in InstantDB.
data.refdata.ref("<path.to.attr>") for linked attributes.Correct
auth.id in data.ref('post.author.id') // auth.id in list of author ids
data.ref('owner.id') == [] // there is no owner
Errors
auth.id in data.post.author.id
auth.id in data.ref('author')
data.ref('admins.id') == auth.id
auth.id == data.ref('owner.id')
data.ref('owner.id') == null
data.ref('owner.id').length > 0
auth.refdata.ref but path must start with $user.Correct
'admin' in auth.ref('$user.role.type')
auth.ref('$user.role.type')[0] == 'admin'
Errors
auth.ref('role.type')
auth.ref('$user.role.type') == 'admin'
newData.ref('x')
data.ref(someVar + '.members.id')
view permission is auth.id == data.idupdate and delete permissions is falsecreate permission is true (anyone can sign up)view, update, and createdeletecreate rule runs during auth signup flows (not via transact). Use it to restrict signups or validate .data.ref does not work for $files permissions.data.path.startsWith(...) or data.path.endsWith(...) to write path-based rules.Restrict access to specific fields while keeping the entity public:
{
"$users": {
"allow": {
"view": "true"
},
"fields": {
"email": "auth.id == data.id"
}
}
}
Notes:
view for that fieldschema when initializing InstantAlways pass schema when initializing Instant to get type safety for queries and transactions
import schema from '@/instant.schema`
// On client
import { init } from '@instantdb/react'; // or your relevant Instant SDK
const clientDb = init({ appId, schema });
// On backend
import { init } from '@instantdb/admin';
const adminDb = init({ appId, adminToken, schema });
id() to generate idsAlways use id() to generate ids for new entities
import { id } from '@instantdb/react'; // or your relevant Instant SDK
import { clientDb } from '@/lib/clientDb
clientDb.transact(clientDb.tx.todos[id()].create({ title: 'New Todo' }));
Always use Instant utility types to type data models
import { AppSchema } from '@/instant.schema';
type Todo = InstaQLEntity<AppSchema, 'todos'>; // todo from clientDb.useQuery({ todos: {} })
type PostsWithProfile = InstaQLEntity<
AppSchema,
'posts',
{ author: { avatar: {} } }
>; // post from clientDb.useQuery({ posts: { author: { avatar: {} } } })
db.useAuth or db.subscribeAuth for auth stateimport { clientDb } from '@/lib/clientDb';
// For react/react-native apps use db.useAuth
function App() {
const { isLoading, user, error } = clientDb.useAuth();
if (isLoading) { return null; }
if (error) { return <Error message={error.message /}></div>; }
if (user) { return <Main />; }
return <Login />;
}
// For vanilla JS apps use db.subscribeAuth
function App() {
renderLoading();
db.subscribeAuth((auth) => {
if (auth.error) { renderAuthError(auth.error.message); }
else if (auth.user) { renderLoggedInPage(auth.user); }
else { renderSignInPage(); }
});
}
extraFieldsPass extraFields to any sign-in method to write custom $users properties atomically on user creation. Fields must be defined as optional attrs on $users in your schema. Use the created boolean to scaffold data for new users.
// Set properties at signup
const { user, created } = await db.auth.signInWithMagicCode({
email,
code,
extraFields: { nickname, createdAt: Date.now() },
});
// Scaffold data for new users
if (created) {
db.transact([
db.tx.settings[id()]
.update({ theme: 'light', notifications: true })
.link({ user: user.id }),
]);
}
Run npx instant-cli query '{ posts: {} }' --admin to query your app. A context flag is required: --admin, --as-email <email>, or --as-guest. Also supports --app <id>.
Use @instantdb/admin to run ad-hoc scripts on the backend. Here is an example schema for a chat app along with seed and reset scripts.
// instant.schema.ts
const _schema = i.schema({
entities: {
$users: i.entity({
email: i.string().unique().indexed().optional(),
}),
profiles: i.entity({
displayName: i.string(),
}),
channels: i.entity({
name: i.string().indexed(),
}),
messages: i.entity({
content: i.string(),
timestamp: i.number().indexed(),
}),
},
links: {
userProfile: {
forward: { on: "profiles", has: "one", label: "user", onDelete: "cascade" }, // IMPORTANT: `cascade` can only be used in a has-one link
reverse: { on: "$users", has: "one", label: "profile" },
},
authorMessages: {
forward: { on: "messages", has: "one", label: "author", onDelete: "cascade" },
reverse: { on: "profiles", has: "many", label: "messages", },
},
channelMessages: {
forward: { on: "messages", has: "one", label: "channel", onDelete: "cascade" },
reverse: { on: "channels", has: "many", label: "messages" },
},
},
});
// scripts/seed.ts
import { id } from "@instantdb/admin";
import { adminDb } from "@/lib/adminDb";
const users: Record<string, User> = { ... }
const channels: Record<string, Channel> = { ... }
const mockMessages: Message[] = [ ... ]
function seed() {
console.log("Seeding db...");
const userTxs = Object.values(users).map(u => adminDb.tx.$users[u.id].create({}));
const profileTxs = Object.values(users).map(u => adminDb.tx.profiles[u.id].create({ displayName: u.displayName }).link({ user: u.id }));
const channelTxs = Object.values(channels).map(c => adminDb.tx.channels[c.id].create({ name: c.name }))
const messageTxs = mockMessages.map(m => {
const messageId = id();
return adminDb.tx.messages[messageId].create({
content: m.content,
timestamp: m.timestamp,
})
.link({ author: users[m.author].id })
.link({ channel: channels[m.channel].id });
})
adminDb.transact([...userTxs, ...profileTxs, ...channelTxs, ...messageTxs]);
}
seed();
// scripts/reset.ts
import { adminDb } from "@/lib/adminDb";
async function reset() {
console.log("Resetting database...");
const { $users, channels } = await adminDb.query({ $users: {}, channels: {} });
// Deleting all users will cascade delete profiles and messages
const userTxs = $users.map(user => adminDb.tx.$users[user.id].delete());
const channelTxs = channels.map(channel => adminDb.tx.channels[channel.id].delete());
adminDb.transact([...userTxs, ...channelTxs]);
}
reset();
The bullets below are links to the Instant documentation. They provide detailed information on how to use different features of InstantDB. Each line follows the pattern of
Fetch the URL for a topic to learn more about it.
Think before you answer. Make sure your code passes typechecks tsc --noEmit and works as expected. Remember! AESTHETICS ARE VERY IMPORTANT. All apps should LOOK AMAZING and have GREAT FUNCTIONALITY!
Weekly Installs
251
Repository
GitHub Stars
1
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex218
opencode216
gemini-cli207
github-copilot202
amp188
kimi-cli187
Supabase Postgres 最佳实践指南 - 8大类别性能优化规则与SQL示例
57,300 周安装
extraFieldsextraFields require an explicit create rule. Without one, signup is blocked to prevent unvalidated writes.