Rust Backend

windmill-labs/windmill/.agents/skills/rust-backend

作者 windmill-labsfb22e5ce368eba9146e0d51b8fdabb52ca9f68f0無授權條款收錄於 2026年10月9日更新於 2026年10月9日

Rust coding guidelines for the Windmill backend. MUST use when writing or modifying Rust code in the backend directory.

AI 產生的概覽

針對 Windmill 後端的 Rust 編碼規範,涵蓋錯誤處理、SQLx、非同步、serde 與 Axum 模式。

功能
提供在 backend 目錄下撰寫或修改 Rust 程式碼時使用的 Windmill 專屬慣例。內容包括錯誤型別與結果別名、SQLx 查詢規則(例如避免 SELECT *、使用批次操作)、JSON 與 serde 處理、非同步與互斥鎖使用建議、模組可見性以及 Axum 處理函式寫法。也說明功能遙測允許清單的行為,並建議使用 rust-analyzer 進行程式碼導覽。
適用情境
在 Windmill 後端目錄中撰寫或修改 Rust 程式碼時使用。適合需要遵循專案在錯誤處理、資料庫存取、並行與 API 處理函式方面既有模式的貢獻者。
執行需求
不包含指令碼,僅為說明性指引。假定專案使用 Rust 及 sqlx、serde、tokio 與 Axum,並建議使用 rust-analyzer LSP 進行導覽。

Windmill Rust Patterns

Apply these Windmill-specific patterns when writing Rust code in backend/.

Error Handling

Use Error from windmill_common::error. Return Result<T, Error> or JsonResult<T>:

rust
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.

SQLx Patterns

Never use SELECT * — always list columns explicitly. Critical for backwards compatibility when workers lag behind API version:

rust
// Correctsqlx::query_as!(Job, "SELECT id, workspace_id, path FROM v2_job WHERE id = $1", id)
// Wrong — breaks when columns are addedsqlx::query_as!(Job, "SELECT * FROM v2_job WHERE id = $1", id)

Use batch operations to avoid N+1:

rust
// Preferred — single query with IN clausesqlx::query!("SELECT ... WHERE id = ANY($1)", &ids[..]).fetch_all(db).await?

Use transactions for multi-step operations. Parameterize all queries.

JSON Handling

Prefer Box<serde_json::value::RawValue> over serde_json::Value when storing/passing JSON without inspection:

rust
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.

Serde Optimizations

rust
#[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,}

Async & Concurrency

Never block the async runtime. Use spawn_blocking for CPU-intensive work:

rust
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.

Module Structure & Visibility

  • Use pub(crate) instead of pub when possible
  • Place new code in the appropriate crate based on functionality
  • API endpoints go in windmill-api/src/ organized by domain
  • Shared functionality goes in windmill-common/src/

Code Navigation

Always use rust-analyzer LSP for go-to-definition, find-references, and type info. Do not guess at module paths.

Feature Telemetry

FEATURE_USAGE_KINDS in windmill-api-workspaces/src/workspaces.rs is an allowlist: a (feature, kind) pair missing from it is dropped by valid_feature_usage_event with a bare continue — no error, and the route still returns 204. Adding a counter on the frontend without registering it here records nothing. See docs/feature-telemetry.md.

Axum Handlers

Destructure extractors directly in function signatures:

rust
async fn process_job(    Extension(db): Extension<DB>,    Path((workspace, job_id)): Path<(String, Uuid)>,    Query(pagination): Query<Pagination>,) -> Result<Json<Job>> { ... }

來源與署名

來源:windmill-labs/windmill位於.agents/skills/rust-backend提交fb22e5c

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架