rust-backend by windmill-labs/windmill
npx skills add https://github.com/windmill-labs/windmill --skill rust-backend在 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() 用于编译时保证的情况。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
切勿使用 SELECT * — 始终明确列出列名。这对于当 worker 落后于 API 版本时保持向后兼容性至关重要:
// 正确
sqlx::query_as!(Job, "SELECT id, workspace_id, path FROM v2_job WHERE id = $1", id)
// 错误 — 添加列时会破坏代码
sqlx::query_as!(Job, "SELECT * FROM v2_job WHERE id = $1", id)
使用批量操作来避免 N+1 问题:
// 推荐 — 使用 IN 子句的单次查询
sqlx::query!("SELECT ... WHERE id = ANY($1)", &ids[..]).fetch_all(db).await?
对于多步骤操作使用事务。对所有查询进行参数化。
当存储或传递 JSON 而不需要检查其内容时,优先使用 Box<serde_json::value::RawValue> 而不是 serde_json::Value:
pub struct Job {
pub args: Option<Box<serde_json::value::RawValue>>,
}
仅在需要检查或修改 JSON 时使用 serde_json::Value。
#[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) 而不是 pubwindmill-api/src/ 中,按领域组织windmill-common/src/ 中始终使用 rust-analyzer LSP 进行跳转到定义、查找引用和类型信息。不要猜测模块路径。
直接在函数签名中解构提取器:
async fn process_job(
Extension(db): Extension<DB>,
Path((workspace, job_id)): Path<(String, Uuid)>,
Query(pagination): Query<Pagination>,
) -> Result<Json<Job>> { ... }
每周安装量
128
代码仓库
GitHub Stars
16.1K
首次出现
2026年1月30日
安全审计
已安装于
opencode124
codex123
gemini-cli122
github-copilot120
kimi-cli120
amp119
Apply these Windmill-specific patterns when writing Rust code in backend/.
Use Error from windmill_common::error. Return Result<T, Error> or 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()))?;
}
Never panic in library code. Reserve .unwrap() for compile-time guarantees.
Never useSELECT * — always list columns explicitly. Critical for backwards compatibility when workers lag behind API version:
// 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)
Use batch operations to avoid N+1:
// Preferred — single query with IN clause
sqlx::query!("SELECT ... WHERE id = ANY($1)", &ids[..]).fetch_all(db).await?
Use transactions for multi-step operations. Parameterize all queries.
Prefer Box<serde_json::value::RawValue> over serde_json::Value when storing/passing JSON without inspection:
pub struct Job {
pub args: Option<Box<serde_json::value::RawValue>>,
}
Only use serde_json::Value when you need to inspect or modify the JSON.
#[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,
}
Never block the async runtime. Use spawn_blocking for CPU-intensive work:
let result = tokio::task::spawn_blocking(move || expensive_computation(&data)).await?;
Mutex selection : Prefer std::sync::Mutex (or parking_lot::Mutex) for data protection. Only use tokio::sync::Mutex when holding locks across .await points.
Use tokio::sync::mpsc (bounded) for channels. Avoid std::thread::sleep in async contexts.
pub(crate) instead of pub when possiblewindmill-api/src/ organized by domainwindmill-common/src/Always use rust-analyzer LSP for go-to-definition, find-references, and type info. Do not guess at module paths.
Destructure extractors directly in function signatures:
async fn process_job(
Extension(db): Extension<DB>,
Path((workspace, job_id)): Path<(String, Uuid)>,
Query(pagination): Query<Pagination>,
) -> Result<Json<Job>> { ... }
Weekly Installs
128
Repository
GitHub Stars
16.1K
First Seen
Jan 30, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode124
codex123
gemini-cli122
github-copilot120
kimi-cli120
amp119
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
116,600 周安装