Your first query
Now let's write a query to fetch users from the database.
Write the query
Add this to .dibs-queries/queries.styx:
styx
@schema { id crate:dibs-queries@1 , cli dibs }
UserByEmail @select {
params { email @string }
from users
where { email $email }
first true
select { id , email , display_name }
}
CreateUser @insert {
params { email @string , name @string }
into users
values { email $email , display_name $name }
returning { id , email , display_name }
} This defines two queries:
UserByEmail: fetch a single user by email (returnsOption<UserByEmailResult>)CreateUser: insert a new user and return the inserted row
Generate the Rust code
bash
cargo build -p my-app-queriesThis runs build.rs, which generates typed Rust functions from your Styx queries.
Use the generated API
In your application code:
rust
use my_app_queries::{ user_by_email, create_user};
use tokio_postgres:: NoTls ;
# [ tokio :: main ]
async fn main () -> Result <(), Box < dyn std:: error:: Error >> {
let ( client, connection) = tokio_postgres:: connect (
& std:: env:: var ( "DATABASE_URL" ) ?,
NoTls ,
). await ?;
tokio:: spawn ( async move {
if let Err ( e) = connection. await {
eprintln! ( "connection error: {}" , e);
}
});
// Create a user
let user = create_user ( & client, "alice@example.com" , "Alice" ). await ?;
println! ( "Created user: {:?}" , user);
// Fetch by email
if let Some ( user) = user_by_email ( & client, "alice@example.com" ). await ? {
println! ( "Found user: {} ({})" , user. display_name , user. email );
}
Ok (())
} LSP support
dibs provides an LSP extension for Styx query files that gives you completions, diagnostics, and go-to-definition when editing .dibs-queries/queries.styx.
Set up Styx for your code editor. When you open .dibs-queries/queries.styx, Styx will detect the @schema declaration and offer to enable the dibs LSP extension via a code action. Accept the prompt to enable completions and diagnostics for your queries.
What gets generated
The generated code includes:
- Result structs with
#[derive(Debug, Clone, Facet)] - Async functions that take
&impl GenericClient - SQL with placeholders already built
- Proper error handling
Example generated code:
rust
# [ derive ( Debug , Clone , Facet )]
pub struct UserByEmailResult {
pub id : i64 ,
pub email : String ,
pub display_name : String ,
}
pub async fn user_by_email < C >(
client : & C ,
email : & str ,
) -> Result < Option < UserByEmailResult >, QueryError >
where
C : tokio_postgres:: GenericClient ,
{
const SQL : & str = r#"SELECT "id", "email", "display_name"
FROM "users"
WHERE "email" = $1"# ;
let rows = client. query ( SQL , & [ & email]). await ?;
match rows. into_iter (). next () {
Some ( row) => Ok ( Some ( from_row ( & row) ?)),
None => Ok ( None ),
}
}