Agent Skills

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

Install

npx skills add https://github.com/windmill-labs/windmill --skill rust-backend
SKILL.md

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.

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:

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

Related skills

typescript-advanced-typeswshobson81KMaster TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.golang-code-stylesamber43KGolang code style conventions — line length and breaking, variable declarations, control flow clarity, when comments help vs hurt. Use when writing or reviewing Go code, asking about style or clarity, or establishing project coding standards. Not for naming conventions (→ See `samber/cc-skills-golang@golang-naming` skill), linter configuration (→ See `samber/cc-skills-golang@golang-lint` skill), or doc comments (→ See `samber/cc-skills-golang@golang-documentation` skill).golang-error-handlingsamber42KIdiomatic Golang error handling — creation, wrapping with %w, errors.Is/As, errors.Join, custom error types, sentinel errors, panic/recover, the single handling rule, structured logging with slog, HTTP request logging middleware, and samber/oops for production errors. Built to make logs usable at scale with log aggregation 3rd-party tools. Apply when creating, wrapping, inspecting, or logging errors in Go code. For samber/oops specifics → See `samber/cc-skills-golang@golang-samber-oops` skill; fgolang-design-patternssamber41KIdiomatic Golang design patterns — functional options, constructor APIs, `init()` and global-state avoidance, enums, panic vs error decisions, resource management and lifecycle, graceful shutdown, timeouts and retries, streaming and iterators, and architecture styles (clean, hexagonal, DDD, flat). Apply when choosing between architectural patterns, implementing functional options, designing constructor APIs, setting up graceful shutdown, applying resilience patterns, or asking which idiomatic Go

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers