{"id":50300259,"url":"https://github.com/josercarmo/sqlx-data","last_synced_at":"2026-05-28T12:30:45.301Z","repository":{"id":358397470,"uuid":"1103667375","full_name":"josercarmo/sqlx-data","owner":"josercarmo","description":"Advanced SQLx companion for type-safe query repositories with automatic parameter binding, sophisticated pagination,   streaming, and zero-boilerplate trait-based implementations.","archived":false,"fork":false,"pushed_at":"2026-05-17T06:32:46.000Z","size":6316,"stargazers_count":15,"open_issues_count":1,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-17T08:39:18.230Z","etag":null,"topics":["database","pagination","repository-pattern","sqlx"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josercarmo.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-25T07:11:14.000Z","updated_at":"2026-05-17T06:32:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/josercarmo/sqlx-data","commit_stats":null,"previous_names":["josercarmo/sqlx-data"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/josercarmo/sqlx-data","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josercarmo%2Fsqlx-data","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josercarmo%2Fsqlx-data/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josercarmo%2Fsqlx-data/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josercarmo%2Fsqlx-data/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josercarmo","download_url":"https://codeload.github.com/josercarmo/sqlx-data/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josercarmo%2Fsqlx-data/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33609237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","pagination","repository-pattern","sqlx"],"created_at":"2026-05-28T12:30:41.312Z","updated_at":"2026-05-28T12:30:45.289Z","avatar_url":"https://github.com/josercarmo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eSQLx-Data\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n \u003cstrong\u003e\n   🏗️ An advanced SQLx companion for type-safe query repositories\n \u003c/strong\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/josercarmo/sqlx-data/raw/HEAD/resources/sqlxdata.jpg\" alt=\"sqlx-data banner\" width=\"50%\"\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003c!-- Crates.io --\u003e\n  \u003ca href=\"https://crates.io/crates/sqlx-data\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/sqlx-data.svg?style=flat-square\"\n    alt=\"Crates.io version\" /\u003e\u003c/a\u003e\n  \u003c!-- Docs --\u003e\n  \u003ca href=\"https://docs.rs/sqlx-data\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square\" alt=\"docs.rs docs\" /\u003e\u003c/a\u003e\n  \u003c!-- Downloads --\u003e\n  \u003ca href=\"https://crates.io/crates/sqlx-data\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/d/sqlx-data.svg?style=flat-square\" alt=\"Download\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- License --\u003e\n  \u003ca href=\"LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square\" alt=\"License\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ch4\u003e\n    \u003ca href=\"#quick-start\"\u003e\n      Quick Start\n    \u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"#features\"\u003e\n      Features\n    \u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://docs.rs/sqlx-data\"\u003e\n      Docs\n    \u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"#examples\"\u003e\n      Examples\n    \u003c/a\u003e\n  \u003c/h4\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\nZero-boilerplate Repository Pattern for modern Rust applications.\n\nAutomatic SQLx parameter binding and result parsing with trait-based repositories. Write SQL traits, get async implementations with sophisticated `pagination`, `streaming`, `batch operations`, and `more`. Seamlessly integrates with existing SQLx code — continue using SQLx queries normally, override generated methods, and reuse generated `_query` methods across different contexts.\n\n```rust\n#[repo]\ntrait UserRepo {\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e;\n\n    #[dml(\"SELECT * FROM users WHERE age \u003e= ?\")]\n    async fn find_adults(\u0026self, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n\n    #[dml(\"SELECT * FROM users ORDER BY id\")]\n    async fn find_all_cursor(\u0026self, params: FilterParams) -\u003e Result\u003cCursor\u003cUser\u003e\u003e;\n\n    #[dml(\"SELECT * FROM users WHERE age \u003e= 18\")]\n    async fn stream_active(\u0026self) -\u003e Result\u003cimpl Stream\u003cItem = Result\u003cUser\u003e\u003e\u003e;\n}\n```\n\n---\n\n## Features\n\n### 🎯 Core\n\n- **Zero boilerplate** — Write traits, not implementations\n- **Compile-time safety** — Always uses SQLx's compile-time macros (`query_as!`, `query!`, `query_scalar!`) ensuring type safety and SQL validation at build time\n- **Smart type inference** — Returns `T`, `Option\u003cT\u003e`, `Vec\u003cT\u003e`, tuples, scalars and others\n- **Multi-database** — PostgreSQL, MySQL, SQLite\n\n### 🚀 Advanced\n\n- **[Pagination \u0026 Dynamic Queries](#pagination--dynamic-queries)**\n  Built-in Serial, Slice, and Cursor strategies. Automatically generates the correct `COUNT(*)` query for pagination metadata and handles complex dynamic filters via `ParamsBuilder`.\n\n- **[Parameter Naming](#parameter-naming)**\n  Use named parameters with `@parameter_name` syntax for cleaner, more readable queries. Parameters can appear in any order and be reused multiple times within the same query.\n\n- **[Aliases](#aliases)**\n  Keep your SQL DRY by defining reusable fragments (like common column sets or table joins) and injecting them using `{{mustache}}` syntax.\n\n- **[Scopes](#scopes)**\n  Rails-inspired query composability. Define reusable `WHERE` clauses or orderings that are **automatically injected** into every query in the repository. Supports Alias interpolation (e.g., `age \u003e {{min_age}}`). Ideal for global patterns like 'SoftDeletable', 'Multi-tenancy', or 'Activable'.\n\n- **[Batch Operations](#batch-operations)**\n  Perform ultra-fast bulk inserts (up to 40x faster) by taking `Vec\u003cT\u003e` arguments, automatically generating efficient multi-value SQL statements.\n\n- **[JSON Support](#json-support)**\n  First-class JSON support. Use the `json` attribute to automatically serialize/deserialize complex Rust structs into database JSON columns without manual boilerplate.\n\n- **[Unchecked Queries](#unchecked-queries)**\n  An escape hatch for DDL, legacy queries, or dynamic SQL that cannot be verified at compile time. Use `unchecked` to bypass the macro's strict validation when necessary.\n\n- **[Method Variants](#method-variants)**\n  Automatically generate `_with_tx`, `_with_conn`, and `_with_pool` variants for every repository method, ensuring you never get stuck with the wrong executor type.\n\n- **[Streaming](#streaming)**\n  Return `impl Stream\u003cItem = Result\u003cT\u003e\u003e` to process large datasets efficiently row-by-row, keeping memory usage constant. Leverages SQLx's native [`fetch`](https://docs.rs/sqlx/latest/sqlx/query/struct.QueryAs.html#method.fetch) for zero-overhead streaming.\n\n- **[Tracing](#tracing)**\n  Zero-config observability powered by the [`tracing`](https://crates.io/crates/tracing) crate. Automatically instruments every query with [`#[instrument]`](https://docs.rs/tracing/latest/tracing/attr.instrument.html) spans, capturing execution time, arguments, and errors.\n\n- **[Generics \u0026 Lifetimes](#generics--lifetimes)**\n  Full support for Rust's generic type system, allowing repositories to handle generic executors, lifetimes, and complex trait bounds.\n\n- **[Hover to inspect](#hover-to-inspect)**\n  Hover over any repository method in your IDE to see the exact SQL query and generated implementation code.\n\n## Feature Flags\n\nSQLx-Data uses feature flags to enable database and type support. **You must specify both a database and typically `json`**:\n\n### Database Features (choose one)\n- `sqlite` — SQLite database support\n- `mysql` — MySQL database support\n- `postgres` — PostgreSQL database support\n\n### Type Features\n- `json` — JSON support with automatic serialization (recommended)\n- `chrono` — Chrono date/time types\n- `time` — Time crate support\n- `uuid` — UUID type support\n- `bigdecimal` — BigDecimal support\n- `rust_decimal` — Rust Decimal support\n- `ipnet` — IP network types\n- `bit-vec` — Bit vector support\n- `mac_address` — MAC address types\n\n### Other Features\n- `tracing` — Automatic query instrumentation\n- `tls-native` — Native TLS support\n- `tls-rustls` — Rustls TLS support\n\n### Example Usage\n```toml\n# For SQLite with JSON\n[dependencies]\nsqlx-data = { version = \"0.1.5\", features = [\"sqlite\", \"json\"] }\nsqlx = { version = \"0.8\", features = [\"sqlite\", \"runtime-tokio\", \"macros\", \"migrate\"] }\n\n# For PostgreSQL with multiple types\n[dependencies]\nsqlx-data = { version = \"0.1.5\", features = [\"postgres\", \"json\", \"chrono\", \"uuid\"] }\nsqlx = { version = \"0.8\", features = [\"postgres\", \"runtime-tokio\", \"macros\", \"migrate\"] }\n\n# For MySQL with tracing\n[dependencies]\nsqlx-data = { version = \"0.1.5\", features = [\"mysql\", \"json\", \"tracing\"] }\nsqlx = { version = \"0.8\", features = [\"mysql\", \"runtime-tokio\", \"macros\", \"migrate\"] }\n```\n\n---\n\n## Quick Start\n\n```toml\n[dependencies]\nsqlx-data = { version = \"0.1.5\", features = [\"sqlite\",\"json\"] }\nsqlx = { version = \"0.8\", features = [\"sqlite\", \"runtime-tokio\"] }\ntokio = { version = \"1\", features = [\"full\"] }\n```\n\n```rust\nuse sqlx_data::{repo, dml, Pool, Result, QueryResult};\n\n#[derive(sqlx::FromRow)]\nstruct User {\n    id: i64,\n    name: String,\n    email: String,\n}\n\n#[repo]\ntrait UserRepo {\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e;\n    \n    #[dml(\"SELECT * FROM users WHERE age \u003e= ?\")]\n    async fn find_adults(\u0026self, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n    \n    #[dml(\"INSERT INTO users (name, email) VALUES (?, ?)\")]\n    async fn create(\u0026self, name: String, email: String) -\u003e Result\u003cQueryResult\u003e;\n}\n\nstruct App { pool: Pool }\n\nimpl UserRepo for App {\n    fn get_pool(\u0026self) -\u003e \u0026Pool { \u0026self.pool }\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c()\u003e {\n    let pool = Pool::connect(\"mysql://...\").await?;\n    let app = App { pool };\n    \n    let user = app.find_by_id(1).await?;\n    println!(\"{}\", user.name);\n    \n    Ok(())\n}\n```\n\n---\n\n## Under the Hood\n\nWhen you define a repository trait with `#[repo]` and `#[dml]`, the macros generate additional `_query` methods that contain the actual SQL execution logic. Your original methods become default implementations that call these generated methods, giving you the flexibility to override them with custom logic.\n\n```rust\n// What you write:\n#[repo]\ntrait UserRepo {\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e;\n\n    #[dml(\"SELECT * FROM users WHERE age \u003e= ?\")]\n    async fn find_adults(\u0026self, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n\n// What gets generated (simplified):\ntrait UserRepo {\n    // Generated _query methods with actual SQL execution\n    async fn find_by_id_query(\u0026self, id: i64) -\u003e Result\u003cUser\u003e {\n        sqlx::query_as!(User, \"SELECT * FROM users WHERE id = ?\", id)\n            .fetch_one(self.get_pool())\n            .await\n    }\n\n    async fn find_adults_query(\u0026self, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e {\n        sqlx::query_as!(User, \"SELECT * FROM users WHERE age \u003e= ?\", min_age)\n            .fetch_all(self.get_pool())\n            .await\n    }\n\n    // Original methods become default implementations\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e {\n        self.find_by_id_query(id).await\n    }\n\n    async fn find_adults(\u0026self, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e {\n        self.find_adults_query(min_age).await\n    }\n\n    // Must be implemented by user\n    fn get_pool(\u0026self) -\u003e \u0026Pool;\n}\n```\n\nThis design allows you to:\n- **Use generated methods directly** by just implementing `get_pool()`\n- **Override specific methods** with custom logic while still calling `_query` methods\n- **Reuse `_query` methods** in different contexts or custom implementations\n\n---\n\n## Pagination \u0026 Dynamic Queries\n\nHandle complex pagination scenarios with the fluent `ParamsBuilder` API.\n\n### 1. Zero-Boilerplate Filters\nCombine pagination, filtering, and sorting in a single object:\n\n```rust\nuse sqlx_data::{Serial, IntoParams, ParamsBuilder, FilterValue};\n\n#[repo]\ntrait UserRepo {\n    // One argument handles everything: defaults + client overrides\n    #[dml(\"SELECT * FROM users\")]\n    async fn find_users(\u0026self, params: impl IntoParams) -\u003e Result\u003cSerial\u003cUser\u003e\u003e;\n}\n\n// Client usage:\nlet params = ParamsBuilder::new()\n    .serial()\n        .page(1, 20)      // Page 1, 20 items per page\n        .done()\n    .filter()\n        .gt(\"age\", 18)    // WHERE age \u003e 18\n        .like(\"name\", \"%Alice%\") // AND name LIKE '%Alice%'\n        .done()\n    .sort()\n        .desc(\"id\")       // ORDER BY id DESC\n        .asc(\"name\")      // THEN BY name ASC\n        .done()\n    .build();\n\nlet result = repo.find_users(params).await?;\n```\n\n### 2. Cursor Pagination (Infinite Scroll)\nBest for high-performance feeds. Supports `after`, `before` based on specific fields.\n\n```rust\nuse sqlx_data::{Cursor, ParamsBuilder};\n\n#[repo]\ntrait FeedRepo {\n    // Automatically handles `before`/`after` cursors based on sorted fields\n    #[dml(\"SELECT * FROM posts\")]\n    async fn user_feed(\u0026self, params: impl IntoParams) -\u003e Result\u003cCursor\u003cPost\u003e\u003e;\n}\n\n// Initial Request:\nlet params = ParamsBuilder::new()\n    .cursor()\n        .first_page()   // Start from beginning\n        .done()\n    .sort()\n        .desc(\"id\")     // Critical: Cursor relies on stable sorting\n        .done()\n    .limit(10)          // Set limit on ParamsBuilder\n    .build();\n\nlet page = repo.user_feed(params).await?;\n\n// Next Page:\nif let Some(next_cursor) = page.next_cursor {\n    let next_params = ParamsBuilder::new()\n        .cursor()\n            .next_cursor::\u003cPost\u003e(\u0026next_cursor) // Type-safe continuation\n            .done()\n        .sort()\n            .desc(\"id\")\n            .done()\n        .limit(10)          // Set limit on ParamsBuilder\n        .build();\n        \n    let next_page = repo.user_feed(next_params).await?;\n}\n```\n\n### 3. Dynamic Search\nBuilt-in text search construction:\n\n```rust\nlet params = ParamsBuilder::new()\n    .slice()\n        .page(1, 50)\n        .done()\n    .search()\n        .query(\"alice\")        // Search term\n        .fields([\"username\", \"email\"]) // Columns to search\n        .case_sensitive(false)\n        .done()\n    .build();\n\n// Generates:\n// WHERE ... AND (username LIKE '%alice%' OR email LIKE '%alice%')\n```\n\n---\n\n## Parameter Naming\n\nClean, readable queries with named parameters using `@parameter_name` syntax. Parameters can be defined in any order and reused multiple times.\n\n### Basic Named Parameters\n```rust\n#[repo]\ntrait UserRepo {\n    // Single named parameter\n    #[dml(\"SELECT * FROM users WHERE name = @name\")]\n    async fn find_by_name(\u0026self, name: String) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n\n    // Multiple named parameters in any order\n    #[dml(\"SELECT * FROM users WHERE age \u003e @min_age AND name LIKE @pattern\")]\n    async fn find_by_age_and_name(\u0026self, pattern: String, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n```\n\n### Parameter Reuse\nSame parameter can be used multiple times in a single query(only for postgres and sqlite):\n```rust\n#[repo]\ntrait UserRepo {\n    // @search_term used twice\n    #[dml(\"SELECT * FROM users WHERE (name = @search_term OR email = @search_term) AND age \u003e @min_age\")]\n    async fn search_user(\u0026self, search_term: String, min_age: u8) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n```\n\n### Mixed with Positional\nNamed parameters work alongside traditional positional parameters:\n```rust\n#[repo]\ntrait UserRepo {\n    // Mix named and positional\n    #[dml(\"SELECT * FROM users WHERE name = @name AND id \u003e $1\")]\n    async fn find_recent_by_name(\u0026self, min_id: i64, name: String) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n```\n\n### Complex Queries\nNamed parameters shine in complex queries with many conditions:\n```rust\n#[repo]\ntrait UserRepo {\n    #[dml(\"\n        SELECT u.*, COUNT(o.id) as order_count\n        FROM users u\n        LEFT JOIN orders o ON u.id = o.user_id\n        WHERE u.age BETWEEN @min_age AND @max_age\n          AND u.created_at \u003e= @start_date\n          AND u.status = @status\n        GROUP BY u.id\n        HAVING COUNT(o.id) \u003e= @min_orders\n        ORDER BY u.created_at DESC\n    \")]\n    async fn find_active_customers(\n        \u0026self,\n        min_age: u8,\n        max_age: u8,\n        start_date: chrono::NaiveDateTime,\n        status: String,\n        min_orders: i32\n    ) -\u003e Result\u003cVec\u003cCustomerStats\u003e\u003e;\n}\n```\n\n## Aliases\n\nReusable SQL fragments for DRY code:\n\n```rust\n```rust\n#[repo]\n#[alias(user_columns = \"id, name, email, age\")]\n#[alias(user_table = \"users\")]\n#[alias(active_filter = \"WHERE age \u003e= 18\")]\ntrait UserRepo {\n    #[dml(\"SELECT {{user_columns}} FROM {{user_table}} {{active_filter}}\")]\n    async fn find_adults(\u0026self) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n    \n    #[dml(\"SELECT COUNT(*) FROM {{user_table}} {{active_filter}}\")]\n    async fn count_adults(\u0026self) -\u003e Result\u003ci64\u003e;\n}\n```\n\n---\n\n## Scopes\n\nAutomatic query enhancement — define once, apply everywhere:\n\n**Pro tip:** Perfect for Rails-like patterns such as multi-tenancy (`tenant_id = ?`), soft deletes (`archived_at IS NULL`), and active records (`status = 'active'`).\n\n```rust\n```rust\n#[repo]\n#[alias(min_age = \"18\")]\n#[scope(adults = \"age \u003e= {{min_age}}\")]\n#[scope(named = \"name IS NOT NULL\")]\n#[scope(recent_birth = \"birth_year \u003e 2000\")]\n#[scope(ordered = \"age DESC\", target = \"order_by\")]\ntrait UserRepo {\n    // All scopes automatically applied\n    #[dml(\"SELECT * FROM users\")]\n    async fn find_all(\u0026self) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n    \n    // Ignore specific scopes when needed\n    #[scope_ignore(ordered)]\n    #[dml(\"SELECT * FROM users ORDER BY name\")]\n    async fn find_alphabetical(\u0026self) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n```\n\n**Generated SQL:**\n```sql\n-- find_all() becomes:\nSELECT * FROM users \nWHERE age \u003e= 18 \n  AND name IS NOT NULL \n  AND birth_year \u003e 2000\nORDER BY age DESC\n\n-- find_alphabetical() becomes:\nSELECT * FROM users \nWHERE age \u003e= 18 \n  AND name IS NOT NULL \n  AND birth_year \u003e 2000\nORDER BY name\n```\n\n---\n\n## Batch Operations\n\nEfficient bulk inserts:\n\n```rust\n#[repo]\ntrait UserRepo {\n    #[dml(\"INSERT INTO users (name, email, age) VALUES\")]\n    async fn insert_batch(\u0026self, rows: Vec\u003c(String, String, u8)\u003e) -\u003e Result\u003cQueryResult\u003e;\n}\n\n// Usage\nlet users = vec![\n    (\"Alice\".into(), \"alice@example.com\".into(), 30),\n    (\"Bob\".into(), \"bob@example.com\".into(), 25),\n    (\"Charlie\".into(), \"charlie@example.com\".into(), 35),\n];\n\nrepo.insert_batch(users).await?;\n```\n\n**Generated Code:**\n```rust\n// The macro generates this optimized batch insert method:\nasync fn insert_batch_query(\u0026self, rows: Vec\u003c(String, String, u8)\u003e) -\u003e Result\u003cQueryResult\u003e {\n    if rows.is_empty() {\n        return Ok(QueryResult::default());\n    }\n\n    // Uses SQLx's efficient QueryBuilder with push_values\n    let mut qb = sqlx::QueryBuilder::new(\"INSERT INTO users (name, email, age) \");\n    qb.push_values(rows, |mut b, tuple| {\n        b.push_bind(tuple.0)    // name\n         .push_bind(tuple.1)    // email\n         .push_bind(tuple.2);   // age\n    });\n\n    qb.build().execute(self.get_pool()).await\n}\n```\n\n**Performance:** Inserts 1000 rows in ~50ms vs ~2000ms with individual inserts\n\n---\n\n### Streaming\nBest for: Large datasets, memory efficiency\n\nUses SQLx's native [`fetch`](https://docs.rs/sqlx/latest/sqlx/query/struct.QueryAs.html#method.fetch) method for zero-overhead row-by-row processing, keeping memory usage constant regardless of result set size.\n\n```rust\nuse futures::Stream;\n\n#[repo]\ntrait UserRepo {\n    // Return impl Stream for memory-efficient processing\n    #[dml(\"SELECT * FROM users WHERE age \u003e= 18\")]\n    fn stream_active(\u0026self) -\u003e impl Stream\u003cItem = Result\u003cUser\u003e\u003e + Send;\n}\n\n// Usage\nlet mut stream = repo.stream_active();\nwhile let Some(user) = stream.next().await {\n    println!(\"Processing {}\", user?.name);\n}\n```\n\n---\n\n### JSON Support\nAutomatic JSON serialization/deserialization:\n\n```rust\nuse sqlx::types::Json;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Serialize, Deserialize)]\nstruct UserProfile {\n    email: String,\n    age: u32,\n    department: String,\n}\n\n#[repo]\ntrait UserRepo {\n    // Automatic JSON serialization for parameters using 'json' attribute\n    #[dml(\"INSERT INTO json_users (name, profile_json) VALUES (?, ?)\", json)]\n    async fn save_profile(\u0026self, name: String, profile: UserProfile) -\u003e Result\u003cQueryResult\u003e;\n\n    // Type-safe JSON retrieval\n    #[dml(\"SELECT id, name, profile_json, preferences FROM json_users WHERE id = ?\")]\n    async fn find_raw_json(\u0026self, id: i64) -\u003e Result\u003cOption\u003c(i64, String, Json\u003cJsonValue\u003e, Option\u003cJsonValue\u003e)\u003e\u003e;\n}\n```\n\n---\n\n### Unchecked Queries\nBypass compile-time verification for complex queries or DDL:\n\n```rust\n#[repo]\ntrait AdminRepo {\n    // Use 'unchecked' to skip SQL validation for dynamic queries\n    #[dml(\"SELECT * FROM information_schema.tables WHERE table_name = ?\", unchecked)]\n    async fn check_table_exists(\u0026self, table_name: String) -\u003e Result\u003cVec\u003cString\u003e\u003e;\n\n    #[dml(\"SELECT * FROM users WHERE id = \" + \"1\", unchecked)]\n    async fn dynamic_query(\u0026self) -\u003e Result\u003cVec\u003cUser\u003e\u003e;\n}\n```\n\n---\n\n### Binary Data (BLOBs)\nEfficient handling of binary data:\n\n```rust\nuse bytes::Bytes;\n\n#[repo]\ntrait FileRepo {\n    #[dml(\"INSERT INTO files (name, content_type, file_size, data, is_compressed) VALUES (?, ?, ?, ?, ?)\")]\n    async fn upload(\n        \u0026self, \n        name: String, \n        content_type: String, \n        file_size: u32, \n        data: bytes::Bytes,\n        is_compressed: bool\n    ) -\u003e Result\u003cQueryResult\u003e;\n\n    #[dml(\"SELECT data FROM files WHERE id = ?\")]\n    async fn download(\u0026self, id: i64) -\u003e Result\u003cVec\u003cu8\u003e\u003e;\n}\n```\n\n---\n\n### Generics \u0026 Lifetimes\nFull support for Rust's type system:\n\n```rust\n#[repo]\ntrait GenericRepo {\n    // Support for lifetime parameters\n    #[dml(\"SELECT * FROM users WHERE name = ?\")]\n    async fn find_by_name\u003c'a\u003e(\u0026self, name: \u0026'a str) -\u003e Result\u003cOption\u003cUser\u003e\u003e;\n\n    // Support for custom executors (transactions, connections)\n    #[dml(\"INSERT INTO logs (msg) VALUES (?)\")]\n    async fn log_with_executor\u003c'e, E\u003e(\u0026self, executor: E, msg: \u0026str) -\u003e Result\u003cQueryResult\u003e\n    where\n        E: sqlx::Executor\u003c'e, Database = sqlx::MySql\u003e;\n}\n```\n\n---\n\n### Method Variants\n\nGenerate multiple executor variants automatically, or pass executors directly as parameters:\n\n```rust\n#[repo]\ntrait UserRepo {\n    #[generate_versions(pool, tx, conn, exec)]\n    #[dml(\"UPDATE users SET name = ? WHERE id = ?\")]\n    async fn update_name(\u0026self, name: String, id: i64) -\u003e Result\u003cQueryResult\u003e;\n}\n\n// Generates 5 methods:\n// - update_name(\u0026self, ...)                           // uses get_pool()\n// - update_name_with_pool(\u0026self, pool: \u0026Pool, ...)    // explicit pool\n// - update_name_with_tx(\u0026self, tx: \u0026mut Transaction, ...)\n// - update_name_with_conn(\u0026self, conn: \u0026mut Connection, ...)\n// - update_name_with_executor(\u0026self, exec: impl Executor, ...)\n\n// Usage\nlet mut tx = pool.begin().await?;\nrepo.update_name_with_tx(\u0026mut tx, \"Alice\".into(), 1).await?;\nrepo.update_name_with_tx(\u0026mut tx, \"Bob\".into(), 2).await?;\ntx.commit().await?;\n```\n\n**Alternative: Direct Executor Parameters**\n\nYou can also pass executors directly as method parameters without code generation:\n\n```rust\n#[repo]\ntrait UserRepo {\n    // Method with explicit pool parameter\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id_with_pool(\u0026self, id: i64, pool: \u0026Pool) -\u003e Result\u003cUser\u003e;\n\n    // Method with transaction parameter\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id_with_tx(\u0026self, id: i64, tx: \u0026mut Transaction\u003c'_\u003e) -\u003e Result\u003cUser\u003e;\n\n    // Method with connection parameter\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    async fn find_by_id_with_conn(\u0026self, id: i64, conn: \u0026mut Connection) -\u003e Result\u003cUser\u003e;\n\n    // Generic executor support\n    #[dml(\"INSERT INTO users (name) VALUES (?)\")]\n    async fn create_user\u003c'e, E\u003e(\u0026self, name: String, executor: impl Executor\u003c'_\u003e) -\u003e Result\u003cQueryResult\u003e;\n}\n\n// Usage\nlet user = repo.find_by_id_with_pool(1, \u0026pool).await?;\nlet user = repo.find_by_id_with_tx(2, \u0026mut tx).await?;\nlet user = repo.find_by_id_with_conn(3, \u0026mut conn).await?;\nrepo.create_user(\"Alice\".into(), \u0026pool).await?;  // Works with any executor\n```\n\n---\n\n## Tracing\n\nBuilt-in observability powered by the [`tracing`](https://crates.io/crates/tracing) library. Zero configuration required to get detailed logs automatically instrumented with [`#[instrument]`](https://docs.rs/tracing/latest/tracing/attr.instrument.html):\n\n```rust\nuse tracing::instrument;\n\n#[repo]\ntrait UserRepo {\n    #[dml(\"SELECT * FROM users WHERE id = ?\")]\n    #[instrument(skip(self))]\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e;\n}\n\n// Automatically logs:\n// - Method entry/exit\n// - Parameters (except skipped ones)\n// - Execution time\n// - Errors\n```\n\n---\n\n## Hover to Inspect\n\nSee the generated SQL and implementation in your IDE:\n\n![Hover to see generated code](https://github.com/josercarmo/sqlx-data/raw/HEAD/resources/hover_to_inspect.gif)\n\n**Pro tip:** Copy the generated code to override methods or call `_query` methods from custom logic:\n\n```rust\nimpl UserRepo for App {\n    fn get_pool(\u0026self) -\u003e \u0026Pool { \u0026self.pool }\n\n    // Override generated method with custom logic\n    async fn find_by_id(\u0026self, id: i64) -\u003e Result\u003cUser\u003e {\n        // Add logging, caching, validation, etc.\n        log::info!(\"Finding user with id: {}\", id);\n        self.find_by_id_query(id).await\n    }\n\n    // Use _query method in custom implementations\n    async fn find_user_with_cache(\u0026self, id: i64) -\u003e Result\u003cUser\u003e {\n        if let Some(cached) = get_from_cache(id) {\n            return Ok(cached);\n        }\n        let user = self.find_by_id_query(id).await?;\n        cache_user(\u0026user);\n        Ok(user)\n    }\n}\n```\n\n---\n\n### Complex Queries\n\n```rust\n#[repo]\ntrait UserRepo {\n    #[dml(\"\n        SELECT \n            u.id,\n            u.name,\n            COUNT(o.id) as order_count,\n            SUM(o.total) as total_spent\n        FROM users u\n        LEFT JOIN orders o ON u.id = o.user_id\n        WHERE u.age \u003e= ?\n        GROUP BY u.id, u.name\n        HAVING COUNT(o.id) \u003e ?\n        ORDER BY total_spent DESC\n    \")]\n    async fn find_top_customers(\n        \u0026self, \n        min_age: u8, \n        min_orders: i32\n    ) -\u003e Result\u003cVec\u003cCustomerStats\u003e\u003e;\n}\n```\n\n### File-based Queries\n\n```rust\n#[repo]\ntrait UserRepo {\n    #[dml(file = \"queries/complex_user_report.sql\")]\n    async fn generate_report(\u0026self) -\u003e Result\u003cVec\u003cReportRow\u003e\u003e;\n}\n```\n\n\n\n---\n\n## Supported Return Types\n\n| Return Type | Example | Fetch Strategy |\n|------------|---------|----------------|\n| `T` | `User` | `fetch_one` |\n| `Option\u003cT\u003e` | `Option\u003cUser\u003e` | `fetch_optional` |\n| `Vec\u003cT\u003e` | `Vec\u003cUser\u003e` | `fetch_all` |\n| Scalar | `i64`, `String`, `bool` | `fetch_one` |\n| Tuple | `(String, i64)` | `fetch_one` |\n| `Vec\u003cTuple\u003e` | `Vec\u003c(String, i64)\u003e` | `fetch_all` |\n| `Serial\u003cT\u003e` | `Serial\u003cUser\u003e` | Paginated |\n| `Slice\u003cT\u003e` | `Slice\u003cUser\u003e` | Paginated |\n| `Cursor\u003cT\u003e` | `Cursor\u003cUser\u003e` | Paginated |\n| Database-specific | `MySqlQueryResult`, `PgQueryResult` | `execute` |\n\n---\n\n## Database Support\n\n| Database | Placeholder | Example |\n|----------|-------------|---------|\n| **MySQL** | `?` | `WHERE id = ?` |\n| **PostgreSQL** | `$1`, `$2` | `WHERE id = $1` |\n| **SQLite** | `$1`, `$2` | `WHERE id = $1` |\n\n---\n\n## Performance\n\n- **Compile-time overhead:** ~20-90µs per query (macro expansion)\n- **Runtime overhead:** Zero — generates the same code you'd write manually\n- **Batch inserts:** 40x faster than individual inserts (1000 rows: 50ms vs 2000ms)\n\n---\n\n## Examples\n\n- [`hello-world`](examples/hello-world) — A minimal setup to get started.\n- [`axum-crud-api`](examples/axum-crud-api) — A full-featured REST API using Axum, showcasing pagination, filtering, and best practices.\n\n**📖 Documentation Book** - [Comprehensive guide](book/src) (under construction)\n\n---\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n---\n\n## License\n\nLicensed under either of:\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))\n- MIT license ([LICENSE-MIT](LICENSE-MIT))\n\nat your option.\n\n---\n\n## Acknowledgments\n\nBuilt on top of the excellent [SQLx](https://github.com/launchbadge/sqlx) library.\nPowered by [syn](https://github.com/dtolnay/syn), [quote](https://github.com/dtolnay/quote), and [proc-macro2](https://github.com/dtolnay/proc-macro2) for macro expansion.\nSQL parsing and validation leverage [sqlparser](https://github.com/sqlparser-rs/sqlparser-rs).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosercarmo%2Fsqlx-data","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosercarmo%2Fsqlx-data","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosercarmo%2Fsqlx-data/lists"}