encore-code-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEncore Code Review
Encore代码审核
Instructions
说明
When reviewing Encore.ts code, check for these common issues:
审核Encore.ts代码时,请检查以下常见问题:
Critical Issues
严重问题
1. Infrastructure Inside Functions
1. 函数内部定义基础设施
typescript
// 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" });typescript
// 错误示例:在函数内部声明基础设施
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" });2. Using require() Instead of import
2. 使用require()而非import
typescript
// WRONG
const { api } = require("encore.dev/api");
// CORRECT
import { api } from "encore.dev/api";typescript
// 错误示例
const { api } = require("encore.dev/api");
// 正确示例
import { api } from "encore.dev/api";3. Wrong Service Import Pattern
3. 错误的服务导入方式
typescript
// 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 });typescript
// 错误示例:直接导入其他服务
import { getUser } from "../user/api";
// 正确示例:使用~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });4. Missing Error Handling
4. 缺少错误处理
typescript
// 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");
}typescript
// 错误示例:未找到时返回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");
}5. SQL Injection Risk
5. SQL注入风险
typescript
// 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}`;typescript
// 错误示例:字符串拼接
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// 正确示例:使用自动转义的模板字面量
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;Warning Issues
警告问题
6. Missing Type Annotations
6. 缺少类型注解
typescript
// 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);
}
);typescript
// 不规范:无显式类型
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);
}
);7. Exposed Internal Endpoints
7. 暴露内部端点
typescript
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
{ expose: true }, // Probably should be false
async () => { /* ... */ }
);typescript
// 检查:这个定时任务端点是否应该暴露?
export const cleanupJob = api(
{ expose: true }, // 通常应该设为false
async () => { /* ... */ }
);8. Non-Idempotent Subscription Handlers
8. 非幂等的订阅处理器
typescript
// 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);
},
});typescript
// 风险:非幂等(发布订阅至少投递一次)
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);
},
});9. Secrets Called at Module Level
9. 模块级别调用密钥
typescript
// 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
}typescript
// 错误示例:启动时访问密钥
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // 导入时调用
// 正确示例:在函数内部访问
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // 运行时调用
}Review Checklist
审核检查清单
- All infrastructure at package level
- Using ES6 imports, not require()
- Cross-service calls use
~encore/clients - Proper error handling with APIError
- SQL uses template literals
- Request/response types defined
- Internal endpoints have
expose: false - Subscription handlers are idempotent
- Secrets accessed inside functions, not at import time
- Migrations follow naming convention (001_name.up.sql)
- 所有基础设施定义在包级别
- 使用ES6导入,而非require()
- 跨服务调用使用
~encore/clients - 使用APIError进行正确的错误处理
- SQL查询使用模板字面量
- 定义了请求/响应类型
- 内部端点设置
expose: false - 订阅处理器是幂等的
- 在函数内部访问密钥,而非导入时
- 迁移文件遵循命名规范(001_name.up.sql)
Output Format
输出格式
When reviewing, report issues as:
[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern
[GOOD] Notable good practice observed审核时,请按以下格式报告问题:
[CRITICAL] [文件:行号] 问题描述
[WARNING] [文件:行号] 关注点描述
[GOOD] 值得注意的最佳实践