Before Hooks and After Hooks
Before Hook and After Hook are the two extension points of Instant API. They let business rules run centrally on the backend, so frontends, Agents, scripts, and third-party systems don't each have to implement their own validation and data-masking logic.
1. Execution Chain
请求进入
-> Before Hook
-> Instant API
-> After Hook
-> 返回结果2. Before Hook
Before Hook runs before the Instant API executes. It's the right place for logic that must stop a non-compliant request from going any further.
Typical scenarios:
| Scenario | Description |
|---|---|
| Uniqueness checks | Phone numbers, order numbers, and external reference numbers must not repeat |
| Permission checks | Whether the current user can run create/update/delete/filter |
| Tenant isolation | Automatically inject or verify tenant_code |
| Default completion | Auto-fill creator, update time, and source |
| State-machine validation | Prevent reverting from completed directly back to in progress |
Example:
export default async function beforeCreate(params, context) {
const existing = await context.client.models.customers.filter({
where: { phone: { $eq: params.phone } },
currentPage: 1,
pageSize: 1,
});
if (existing.tableData?.length) {
throw new Error("手机号已存在");
}
return {
...params,
tenant_code: context.tenantCode,
created_by: context.userInfo?.id,
};
}3. After Hook
After Hook runs once the Instant API has its result. Use it for processing that should be applied uniformly before the result goes back to the caller.
Typical scenarios:
| Scenario | Description |
|---|---|
| Data masking | Handle sensitive fields such as phone numbers, ID numbers, and email addresses |
| Field enrichment | Status labels, display fields, derived fields |
| Reshaping | Restructure the runtime result into a shape that pages consume more easily |
| Consistent output | Ensure pages, Agents, and third-party systems see the same result |
Example:
export default async function afterFilter(result) {
const rows = result.tableData ?? [];
return {
...result,
tableData: rows.map((row) => ({
...row,
phone: row.phone ? row.phone.replace(/(\\d{3})\\d{4}(\\d{4})/, "$1****$2") : row.phone,
statusText: row.status === "active" ? "已启用" : "未启用",
})),
};
}4. Boundary Between Endpoints and Hooks
| Capability | What belongs there |
|---|---|
| Before Hook | Validation, authorization, and default values before a single Instant API runs |
| After Hook | Masking, field enrichment, and formatting before a single Instant API returns |
| Endpoint | Multi-step business orchestration, transactions, external system calls, callback handling |
| Common | Pure functions and utilities shared by multiple Hooks and Endpoints |
Don't force complex business flows into Hooks. Keep Hooks short, stable, and predictable; put complex flows in an Endpoint and let the Endpoint call Instant APIs.
5. Best Practices
- Don't duplicate large chunks of business code across Hooks — factor them into Common.
- When a Before Hook fails, return a clear error so frontends and Agents can surface it.
- Avoid slow external calls in After Hooks — they drag down list queries.
- Test Hooks on write operations, especially the edge cases of create/update/delete.