rust-backend

Windmill Rust Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rust-backend" with this command: npx skills add windmill-labs/windmill/windmill-labs-windmill-rust-backend

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> :

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:

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

JSON Handling

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.

Serde Optimizations

#[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:

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.

Axum Handlers

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>> { ... }

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

svelte-frontend

No summary provided by upstream source.

Repository SourceNeeds Review
General

commit

No summary provided by upstream source.

Repository SourceNeeds Review
General

pr

No summary provided by upstream source.

Repository SourceNeeds Review