关于 rust-backend
面向 Windmill backend/ 中 Rust 开发的模式指南,覆盖错误处理、SQLx、JSON、异步并发和模块组织。还包含代码导航与功能遥测规则。
主要功能
- 01Result 错误处理与避免 panic
- 02SQLx 查询和事务模式
- 03JSON 与 Serde 处理建议
- 04异步并发与锁选择
- 05模块组织和 rust-analyzer 导航
- 06功能遥测与 Axum 处理器规范
适用场景
- 01实现 Windmill backend/ API
- 02编写兼顾兼容性的 SQLx 查询
- 03检查异步任务和并发代码
- 04新增前端功能使用遥测事件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ windmill-labs/ windmill --skill rust-backend安装后在 Claude Code 中输入/rust-backend确认已加载。
详细介绍
Windmill Rust 模式
在 backend/ 中编写 Rust 代码时,应用以下 Windmill 专属模式。
错误处理
使用 windmill_common::error 中的 Error。返回 Result<T, Error> 或 JsonResult<T>:
use windmill_common::error::{Error, Result};
pub async fn get_job(db: &DB, id: Uuid) -> Result<Job> {
sqlx::query_as!(Job, "SELECT id, workspace_id FROM v2_job WHERE id = $1", id)
.fetch_optional(db)
.await?
.ok_or_else(|| Error::NotFound("job not found".to_string()))?;
}
库代码中绝不要 panic。仅在编译期保证成立时使用 .unwrap()。
SQLx 模式
绝不要使用 SELECT *——始终显式列出列名。这对向后兼容至关重要,因为 worker 可能落后于 API 版本:
// Correct
sqlx::query_as!(Job, "SELECT id, workspace_id, path FROM v2_job WHERE id = $1", id)
// Wrong — breaks when columns are added
sqlx::query_as!(Job, "SELECT * FROM v2_job WHERE id = $1", id)
使用批量操作避免 N+1:
// Preferred — single query with IN clause
sqlx::query!("SELECT ... WHERE id = ANY($1)", &ids[..]).fetch_all(db).await?
多步操作使用事务。所有查询都要参数化。
JSON 处理
在存储或传递 JSON、但不检查其内容时,优先使用 Box<serde_json::value::RawValue>,而不是 serde_json::Value:
pub struct Job {
pub args: Option<Box<serde_json::value::RawValue>>,
}
只有在需要检查或修改 JSON 时,才使用 serde_json::Value。
Serde 优化
#[derive(Serialize, Deserialize)]
pub struct Job {
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_job: Option<Uuid>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default)]
pub priority: i32,
}
异步与并发
绝不要阻塞异步运行时。对于 CPU 密集型工作,使用 spawn_blocking:
let result = tokio::task::spawn_blocking(move || expensive_computation(&data)).await?;
互斥锁选择:优先使用 std::sync::Mutex(或 parking_lot::Mutex)保护数据。仅在需要跨 .await 点持有锁时,才使用 tokio::sync::Mutex。
使用 tokio::sync::mpsc(有界)作为通道。避免在异步上下文中使用 std::thread::sleep。
模块结构与可见性
- 尽可能使用
pub(crate),而不是pub - 根据功能将新代码放在适当的 crate 中
- API 端点放在
windmill-api/src/中,并按领域组织 - 共享功能放在
windmill-common/src/中
代码导航
始终使用 rust-analyzer LSP 进行转到定义、查找引用和类型信息查询。不要猜测模块路径。
功能遥测
windmill-api-workspaces/src/workspaces.rs 中的 FEATURE_USAGE_KINDS 是一个允许列表:若其中缺少 (feature, kind) 对,valid_feature_usage_event 就会通过单独的 continue 将其丢弃——不会报错,路由仍会返回 204。在前端添加计数器却未在此处注册,不会记录任何内容。请参阅 docs/feature-telemetry.md。
Axum 处理器
在函数签名中直接解构提取器:
async fn process_job(
Extension(db): Extension<DB>,
Path((workspace, job_id)): Path<(String, Uuid)>,
Query(pagination): Query<Pagination>,
) -> Result<Json<Job>> { ... }