npx skills add https://github.com/encoredev/skills --skill encore-code-review审查 Encore.ts 代码时,请检查以下常见问题:
// 错误:在函数内部声明基础设施
async function setup() {
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}
// 正确:包级别声明
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
// 错误
const { api } = require("encore.dev/api");
// 正确
import { api } from "encore.dev/api";
// 错误:直接从另一个服务导入
import { getUser } from "../user/api";
// 正确:使用 ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
// 错误:未找到时返回 null
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;
// 正确:抛出 APIError
import { APIError } from "encore.dev/api";
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
throw APIError.notFound("user not found");
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// 错误:字符串拼接
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// 正确:使用自动转义的模板字面量
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
// 较弱:没有显式类型
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }) => {
return await findUser(id);
}
);
// 更好:显式定义请求/响应类型
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
return await findUser(id);
}
);
// 检查:这个 cron 端点应该暴露吗?
export const cleanupJob = api(
{ expose: true }, // 可能应该设为 false
async () => { /* ... */ }
);
// 有风险:非幂等(pubsub 提供至少一次交付保证)
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
await chargeCustomer(event.orderId); // 可能重复扣款!
},
});
// 更安全:处理前检查
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
const order = await getOrder(event.orderId);
if (order.status !== "pending") return; // 已处理过
await chargeCustomer(event.orderId);
},
});
// 错误:在启动时访问密钥
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // 在导入时调用
// 正确:在函数内部访问
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // 在运行时调用
}
~encore/clientsexpose: false审查时,按以下格式报告问题:
[关键] [文件:行号] 问题描述
[警告] [文件:行号] 关注点描述
[良好] 观察到的显著良好实践
每周安装量
179
代码仓库
GitHub 星标数
20
首次出现
2026年1月21日
安全审计
安装于
codex141
opencode137
gemini-cli135
github-copilot119
claude-code117
cursor109
When reviewing Encore.ts code, check for these common issues:
// WRONG: Infrastructure declared inside function
async function setup() {
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}
// CORRECT: Package level declaration
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
// WRONG
const { api } = require("encore.dev/api");
// CORRECT
import { api } from "encore.dev/api";
// WRONG: Direct import from another service
import { getUser } from "../user/api";
// CORRECT: Use ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
// WRONG: Returning null for not found
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;
// CORRECT: Throw APIError
import { APIError } from "encore.dev/api";
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
throw APIError.notFound("user not found");
}
// WRONG: String concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// CORRECT: Template literal with automatic escaping
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
// WEAK: No explicit types
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }) => {
return await findUser(id);
}
);
// BETTER: Explicit request/response types
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
return await findUser(id);
}
);
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
{ expose: true }, // Probably should be false
async () => { /* ... */ }
);
// RISKY: Not idempotent (pubsub has at-least-once delivery)
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
await chargeCustomer(event.orderId); // Could charge twice!
},
});
// SAFER: Check before processing
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
const order = await getOrder(event.orderId);
if (order.status !== "pending") return; // Already processed
await chargeCustomer(event.orderId);
},
});
// WRONG: Secret accessed at startup
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // Called during import
// CORRECT: Access inside functions
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // Called at runtime
}
~encore/clientsexpose: falseWhen reviewing, report issues as:
[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern
[GOOD] Notable good practice observed
Weekly Installs
179
Repository
GitHub Stars
20
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex141
opencode137
gemini-cli135
github-copilot119
claude-code117
cursor109
Supabase Postgres 最佳实践指南 - 8大类别性能优化规则与SQL示例
70,900 周安装