Skip to content

Quickstart

A 5-minute walkthrough from zero to generated code.

1. Create a Schema

Create sql/schema.sql:

CREATE TABLE users (
    id    SERIAL PRIMARY KEY,
    name  TEXT NOT NULL,
    email TEXT NOT NULL UNIQUE,
    status TEXT NOT NULL DEFAULT 'active'
);

CREATE TABLE orders (
    id      SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id),
    total   NUMERIC(10, 2) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

2. Write Annotated Queries

Create sql/queries.sql:

-- @name GetUser
-- @returns :one
SELECT id, name, email, status
FROM users
WHERE id = $1;

-- @name ListUsers
-- @returns :many
SELECT id, name, email, status
FROM users
WHERE status = $1
ORDER BY name;

-- @name CreateUser
-- @returns :exec
INSERT INTO users (name, email)
VALUES ($1, $2);

3. Create scythe.toml

[scythe]
version = "1"

[[sql]]
name = "main"
engine = "postgresql"
schema = ["sql/schema.sql"]
queries = ["sql/queries.sql"]
output = "src/generated"

4. Generate Code

scythe generate

Output:

[main] Parsing schema...
[main] Analyzing 3 queries...
[main] Writing output to src/generated/queries.rs
Done.

5. Generated Code (Rust)

src/generated/queries.rs:

// Auto-generated by scythe. Do not edit.

#[derive(Debug, sqlx::FromRow)]
pub struct GetUserRow {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub status: String,
}

pub async fn get_user(pool: &sqlx::PgPool, id: i32) -> Result<GetUserRow, sqlx::Error> {
    sqlx::query_as!(GetUserRow, "SELECT id, name, email, status FROM users WHERE id = $1", id)
        .fetch_one(pool)
        .await
}

#[derive(Debug, sqlx::FromRow)]
pub struct ListUsersRow {
    pub id: i32,
    pub name: String,
    pub email: String,
    pub status: String,
}

pub async fn list_users(pool: &sqlx::PgPool, status: String) -> Result<Vec<ListUsersRow>, sqlx::Error> {
    sqlx::query_as!(ListUsersRow, "SELECT id, name, email, status FROM users WHERE status = $1 ORDER BY name", status)
        .fetch_all(pool)
        .await
}

pub async fn create_user(pool: &sqlx::PgPool, name: String, email: String) -> Result<(), sqlx::Error> {
    sqlx::query!("INSERT INTO users (name, email) VALUES ($1, $2)", name, email)
        .execute(pool)
        .await?;
    Ok(())
}

6. Validate and Lint

# Validate SQL parses and types resolve correctly
scythe check

# Lint SQL for correctness, performance, and style
scythe lint

Next Steps