Skip to content

Sidecar Services

mik-sdk handlers are designed to work with external services for data storage, caching, and other infrastructure concerns. This guide covers patterns for integrating with these services.

mik-sdk includes a SQL query builder for generating parameterized queries. The queries are executed by your sidecar database service.

use mik_sdk::prelude::*;
// SELECT
let (sql, params) = sql_read!(users {
select: [id, name, email, created_at],
filter: { active: true },
order: name,
limit: 20,
});
// sql: SELECT id, name, email, created_at FROM users WHERE active = $1 ORDER BY name LIMIT 20
// params: [Value::Bool(true)]
// INSERT
let (sql, params) = sql_create!(users {
name: "Alice",
email: "alice@example.com",
returning: [id, created_at],
});
// sql: INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, created_at
// UPDATE
let (sql, params) = sql_update!(users {
set: { name: "Bob" },
filter: { id: 123 },
});
// sql: UPDATE users SET name = $1 WHERE id = $2
// DELETE
let (sql, params) = sql_delete!(users {
filter: { id: 123 },
});
// sql: DELETE FROM users WHERE id = $1
// Comparison operators
sql_read!(users {
filter: {
age: { $gte: 18 }, // age >= 18
status: { $ne: "banned" }, // status != 'banned'
score: { $lt: 100 }, // score < 100
},
});
// List operators
sql_read!(users {
filter: {
role: { $in: ["admin", "moderator"] },
id: { $nin: [1, 2, 3] },
},
});
// Text operators
sql_read!(users {
filter: {
name: { $like: "%alice%" },
email: { $ends_with: "@example.com" },
bio: { $contains: "developer" },
},
});
// Logical operators
sql_read!(users {
filter: {
$or: [
{ role: "admin" },
{ verified: true }
]
},
});
OperatorSQLExample
$eq={ age: { $eq: 18 } }
$ne!={ status: { $ne: "banned" } }
$gt>{ score: { $gt: 100 } }
$gte>={ age: { $gte: 18 } }
$lt<{ score: { $lt: 50 } }
$lte<={ age: { $lte: 65 } }
$inIN{ id: { $in: [1, 2, 3] } }
$ninNOT IN{ id: { $nin: [1, 2, 3] } }
$likeLIKE{ name: { $like: "%alice%" } }
$starts_withLIKE 'x%'{ name: { $starts_with: "A" } }
$ends_withLIKE '%x'{ email: { $ends_with: ".com" } }
$containsLIKE '%x%'{ bio: { $contains: "rust" } }
$betweenBETWEEN{ age: { $between: [18, 65] } }
sql_read!(posts {
select: [id, title, created_at],
order: [-created_at, id], // DESC created_at, ASC id
});

Prefix with - for descending order.

#[derive(Query)]
pub struct PageQuery {
#[field(default = 1)]
pub page: u32,
#[field(default = 20, max = 100)]
pub limit: u32,
}
fn list_users(query: PageQuery, _req: &Request) -> Response {
let (sql, params) = sql_read!(users {
select: [id, name],
order: id,
page: query.page,
limit: query.limit,
});
// Execute query via sidecar...
ok!({ "sql": sql })
}

For SQLite, add sqlite as the first parameter:

let (sql, params) = sql_read!(sqlite, users {
filter: { active: true },
});
// Uses ?1, ?2 placeholders instead of $1, $2

The HTTP client is included by default. Use fetch! to call external services.

use mik_sdk::prelude::*;
fn list_users(_req: &Request) -> Response {
let (sql, params) = sql_read!(users {
select: [id, name, email],
limit: 20,
});
// Call database proxy sidecar
let response = fetch!(POST "http://db-proxy:8080/query", json: {
"sql": sql,
"params": params
}).send()?;
if response.is_success() {
ok!({ "data": response.json() })
} else {
error! {
status: 500,
title: "Database Error",
detail: "Failed to execute query"
}
}
}
fn get_user(path: Id, _req: &Request) -> Response {
let user_id = path.as_str();
// Try cache first
let cache_response = fetch!(GET format!("http://cache:8080/users/{}", user_id))
.send()
.ok();
if let Some(resp) = cache_response {
if resp.is_success() {
// Return cached data
return ok!({ "data": resp.json(), "cached": true });
}
}
// Cache miss - fetch from database
let db_response = fetch!(GET format!("http://db-proxy:8080/users/{}", user_id))
.send()?;
if db_response.is_success() {
// Store in cache (fire and forget)
let _ = fetch!(PUT format!("http://cache:8080/users/{}", user_id),
body: db_response.bytes()
).send();
ok!({ "data": db_response.json(), "cached": false })
} else {
not_found!("User not found")
}
}

Use the ids! macro to extract IDs for batched queries:

fn list_posts_with_authors(_req: &Request) -> Response {
// Get posts
let (sql, params) = sql_read!(posts {
select: [id, title, author_id],
limit: 20,
});
// let posts = execute_query(sql, params);
// Extract author IDs for batched loading
// let author_ids = ids!(posts, author_id);
// Batch load authors (single query, no N+1)
// let (sql, params) = sql_read!(users {
// filter: { id: { $in: author_ids } },
// });
// let authors = execute_query(sql, params);
ok!({ "message": "Batched loading pattern" })
}