Patterns · 1 of 4

Input validation

Procedures run server-side, so they're the last gate before data hits storage. Validate everything at the top — fail fast, fail clearly.

Guard-clause style

proc create_user(email, name, age) {
    if email == null || email == "" { abort "email required" }
    if name == null  || name == ""  { abort "name required" }
    if age < 13                    { abort "must be 13 or older" }
    insert("users", {email: email, name: name, age: age})
    return {ok: true}
}

Range checks

proc set_price(sku, price) {
    if price < 0      { abort "price cannot be negative" }
    if price > 10000  { abort "price too high — needs approval" }
    update("products", {sku: sku}, {$set: {price: price}})
    return {ok: true}
}

Existence checks

proc add_to_team(user_id, team_id) {
    let user = find_one("users", {_id: user_id})
    let team = find_one("teams", {_id: team_id})
    if user == null { abort "user not found" }
    if team == null { abort "team not found" }
    insert("members", {user_id: user_id, team_id: team_id})
    return {ok: true}
}

Permission checks

proc delete_post(post_id, actor_id) {
    let post = find_one("posts", {_id: post_id})
    if post == null { abort "post not found" }

    let actor = find_one("users", {_id: actor_id})
    if actor == null { abort "actor not found" }

    if post.author_id != actor_id && actor.role != "admin" {
        abort "forbidden"
    }
    delete_one("posts", {_id: post_id})
    return {ok: true}
}

Uniqueness checks

proc claim_username(user_id, username) {
    if count("users", {username: username}) > 0 {
        abort "username taken"
    }
    update("users", {_id: user_id}, {$set: {username: username}})
    return {ok: true}
}

Quota checks

proc upload(user_id, file_size) {
    let user = find_one("users", {_id: user_id})
    if user.storage_used + file_size > user.storage_quota {
        abort "storage quota exceeded"
    }
    update("users", {_id: user_id}, {$inc: {storage_used: file_size}})
    insert("files", {owner: user_id, size: file_size})
    return {ok: true, used: user.storage_used + file_size}
}

Compose validation into its own proc

proc validate_email(email) {
    if email == null || email == "" { abort "email required" }
    return {ok: true}
}

proc signup(email, name) {
    validate_email({email: email})
    if count("users", {email: email}) > 0 { abort "email taken" }
    insert("users", {email: email, name: name})
    return {ok: true}
}
Error message style: short, lowercase, machine-grep-friendly. Wire response is {"ok": false, "error": "msg"}; clients can switch on the message text.
Report Issue