一句话:用 <script src="https://lingcode.dev/sdk/lingcode-v1.js"></script> 加载 SDK,用后端的数据 URL 和匿名密钥创建一个客户端(两者都可从 lingcode.dev/backends → 你的后端 → 设置 / 连接详情 复制),然后调用 lingcode.from('todos').select()、.auth.signIn(...)、.storage 和实时功能。在 /try 里,客户端已经以 window.lingcode 的形式预注入;在你自己的应用中,则需要你亲手接好这两个值。要记住一条规则:数据 API 是表级 CRUD,不是原始 SQL——不支持 JOIN、不支持 upsert,每页 200 行。
如果你在 /try 或 Mac IDE 里构建了应用,后端客户端是直接递给你的——window.lingcode 就那么存在着。可一旦你把这个应用搬到别处——你自己的 Next.js 项目、一个纯 HTML 页面、一个 Vite 构建——你就得亲手把后端接起来。它只是两个值加一个 script 标签,本教程会把整个流程走一遍,包括那条会绊倒从原始 SQL 过来的人的设计约束。
一个 LingCode Cloud 后端就是一个私有 Postgres 数据库(外加认证、存储、实时和向量搜索),你通过 HTTPS 访问它。你不会从浏览器打开数据库连接或运行 SQL——你通过官方客户端 SDK 与一个小型数据网关对话,这个 SDK 的形态类似 Supabase 或 Firebase,所以调用方式让人感到熟悉。下面所有内容都是这个 SDK。
/try 之外加载 SDK 并创建客户端每个后端都正好有两样客户端需要的东西:
https://lingcode.dev/api/cloud/be/<your-backend-id>。这是这个后端的网关。两者都能在 lingcode.dev/backends 找到——打开你的后端,从设置里复制连接详情(和你粘贴到其他 MCP 客户端里的是同一对)。在框架中,把它们存为公开配置——对于 Next.js,是 NEXT_PUBLIC_LINGCODE_BACKEND_URL 和 NEXT_PUBLIC_LINGCODE_BACKEND_ANON_KEY;对于打包工具,就按你的 import.meta.env / process.env 约定来。它们是公开的,所以应该放在客户端配置里,而不是只在服务端可见的密钥里。
lingcode.functions.invoke(...))来使用,函数在服务端运行。任何客户端能看到的东西,都当作公开内容来对待。
SDK 是从 CDN 提供的单个零依赖文件。纯 HTML 版本:
<script src="https://lingcode.dev/sdk/lingcode-v1.js"></script>
<script>
const lingcode = LingCode.createClient(
'https://lingcode.dev/api/cloud/be/<your-backend-id>',
'<your-anon-key>'
);
</script>
在模块/打包工具的应用中,加载同一个文件,并用你的环境变量值调用 LingCode.createClient(url, key)。(在 /try 和 Mac 预览里你完全跳过这一步——客户端已经以 window.lingcode 的形式存在,预先接好了那个项目的后端。绝不要在那里重新创建它或硬编码密钥。)
在读取认证状态之前,先等客户端在加载时稳定一次——它会先把任何登录重定向处理完:
await lingcode.ready;
const user = lingcode.auth.getUser(); // { id, email } | null
每次调用都返回 { data, error }——使用 data 之前先检查 error。过滤器链接在动词之前:
// 读取
const { data, error } = await lingcode
.from('todos')
.eq('done', false)
.order('created_at', { ascending: false })
.limit(50)
.select();
// 写入
await lingcode.from('todos').insert({ title: 'Buy milk' });
await lingcode.from('todos').eq('id', 1).update({ done: true }); // 必须有过滤器
await lingcode.from('todos').eq('id', 1).delete(); // 必须有过滤器
过滤运算符:.eq .neq .gt .gte .lt .lte .like .ilike .in(col,[…]) .is(col,null),以及一次性多个等值匹配的 .match({a:1,b:2})。多个过滤器以 AND 连接。update 和 delete 在没有过滤器时拒绝执行,所以你不会意外清空一张表。
lingcode.auth.signUp({email,password}) / signIn(...);免密码 sendMagicLink({email})(SDK 会自动完成返回的链接);邮箱验证码 sendOtp + verifyOtp;社交登录先 getProviders() 再 signInWithOAuth('google')。SDK 会存储会话并附加到之后的调用上,因此后续的 .from() 读取会以登录用户的身份运行,RLS 也据其 id 生效。await lingcode.storage.from('public').upload('avatars/me.png', file) → data.url;getPublicUrl(path);download(path)。const off = lingcode.from('todos').subscribe(({type,row}) => { /* INSERT|UPDATE|DELETE */ });在销毁时调用 off()。事件经过 RLS 过滤,所以登录用户只会收到属于自己的行。用它来取代轮询。embedding 列,用 lingcode.vector.search({ table, column, embedding, limit, metric:'cosine' }) 按相似度排序,用于语义搜索 / RAG。这是会让从 SQL 客户端或 PostgREST 过来的人意外的约束,也是"代码一次就跑通"和"代码报 400"之间的分水岭。运行时数据 API 一次只操作一张表。所以:
user_id,再读取那些 users,最后在客户端拼起来。ON CONFLICT。先 insert,如果遇到重复键错误,就取出那一行改用 update。.select() 最多返回 200 行——用 .limit() / .offset() 分页,并在你的代码里计算计数和求和。任何真正涉及关系或较重的操作——跨表报表、索引、约束、生成列、种子数据——都属于 schema,而不是运行时调用。创建一个 VIEW(然后像查表一样从中 select())、添加索引,或者从控制台的 SQL 标签页或 apply_migration 工具运行迁移。网关被有意保持为一个轻薄、安全的 CRUD 层;Postgres 的全部能力就在下面一层,在你的迁移里。
SDK 有意做成形态类似 Supabase,所以大多数调用点都能一一对应。对不上的只有上面那两条规则:把 .select('*, author(*)') 的 JOIN 改写为分别取数据,把 .upsert() 改写为先 insert 再 update。在 Mac IDE 或 /try 里,只需让 agent "把我的 Supabase 项目迁移到 LingCode 并部署"——迁移技能会替你处理 schema、数据以及这些改写。