rust-web-server 17.48.0

An HTTP web framework, reverse proxy, and server for Rust supporting HTTP/1.1, HTTP/2, and HTTP/3. Config-driven proxy mode (rws.config.toml with [[route]] / [[upstream]]) or library crate. No third-party HTTP dependencies.
Documentation
---
title: Repository
description: Async CRUD operations for a model type via the Repository trait and ModelRepository.
---

`ModelRepository` implements the `Repository<T, ID>` trait and provides the standard set of async create, read, update, and delete operations for a model type. Obtain one from any `#[derive(Model)]` struct using `User::repository(&pool)`.

## Obtaining a repository

```rust
use rust_web_server::model::DbPool;

let pool = DbPool::from_env().await?;

// User::repository is generated by #[derive(Model)]
let repo = User::repository(&pool);
```

The repository borrows the pool by reference for its lifetime — the pool itself is shared and async-safe.

## `Repository<T, ID>` trait

```rust
pub trait Repository<T: Model, ID> {
    async fn find_by_id(&self, id: ID) -> Result<Option<T>, DbError>;
    async fn find_all(&self) -> Result<Vec<T>, DbError>;
    async fn save(&self, entity: &T) -> Result<T, DbError>;
    async fn save_all(&self, entities: &[T]) -> Result<Vec<T>, DbError>;
    async fn delete_by_id(&self, id: ID) -> Result<(), DbError>;
    async fn delete_all_by_id(&self, ids: &[ID]) -> Result<(), DbError>;
    async fn count(&self) -> Result<i64, DbError>;
    async fn exists_by_id(&self, id: ID) -> Result<bool, DbError>;
}
```

The `ID` type is always `i64` for the built-in `ModelRepository` implementation.

## Method reference

### `find_by_id`

Executes `SELECT * FROM users WHERE id = ?` and returns the first matching row, or `None` if no row exists.

```rust
match repo.find_by_id(42).await? {
    Some(user) => println!("found: {}", user.email),
    None       => println!("not found"),
}
```

### `find_all`

Executes `SELECT * FROM users` and returns every row in the table.

```rust
let all_users: Vec<User> = repo.find_all().await?;
```

Use the [Query Builder](/database/query-builder/) when you need filtering, ordering, or pagination.

### `save`

Inserts or updates a single entity.

- **INSERT** — when the primary key field is `0` (or `Value::Null`).
- **UPDATE** — when the primary key is non-zero.

`save` returns the persisted entity with the primary key filled in (important for auto-increment inserts).

```rust
let saved = repo.save(&User {
    id:    0,   // 0 triggers INSERT with auto_increment
    name:  "Alice".into(),
    email: "alice@example.com".into(),
    role:  "user".into(),
    active: true,
}).await?;
println!("new id: {}", saved.id);   // id assigned by the database
```

To update, set a non-zero primary key:

```rust
let updated = repo.save(&User { id: saved.id, role: "admin".into(), ..saved }).await?;
```

### `save_all`

Calls `save` for each entity in the slice, returning a `Vec<T>` of the persisted entities.

```rust
let users = vec![
    User { id: 0, name: "Bob".into(),   email: "bob@example.com".into(),   .. },
    User { id: 0, name: "Carol".into(), email: "carol@example.com".into(), .. },
];
let saved = repo.save_all(&users).await?;
```

### `delete_by_id`

Executes `DELETE FROM users WHERE id = ?`.

```rust
repo.delete_by_id(42).await?;
```

### `delete_all_by_id`

Calls `delete_by_id` for each id in the slice (one DELETE per id).

```rust
repo.delete_all_by_id(&[1, 2, 3]).await?;
```

### `count`

Executes `SELECT COUNT(*) FROM users` and returns the result as `i64`.

```rust
let total: i64 = repo.count().await?;
println!("{total} users in database");
```

### `exists_by_id`

Returns `true` if a row with the given id exists.

```rust
if repo.exists_by_id(99).await? {
    println!("user 99 exists");
}
```

Internally calls `find_by_id` and checks for `Some`.

## Auto-increment PK retrieval

After an INSERT, the newly assigned primary key is retrieved differently per backend:

| Backend | Mechanism |
|---|---|
| SQLite | `last_insert_rowid()` |
| PostgreSQL | `INSERT … RETURNING id` |
| MySQL | `last_insert_id()` |

In all cases, `save` re-fetches the inserted row via `SELECT * FROM … WHERE id = ?` and returns the complete, database-consistent entity.

## Full CRUD example

```rust
use rust_web_server::model::{DbPool, Repository};
use rust_web_server::Model;

#[derive(Model, Debug, Clone)]
#[table(name = "users")]
pub struct User {
    #[primary_key(auto_increment)]
    pub id: i64,
    #[column(name = "first_name")]
    pub name: String,
    #[column(unique)]
    pub email: String,
    pub role: String,
    pub active: bool,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pool = DbPool::from_env().await?;
    let repo = User::repository(&pool);

    // CREATE
    let alice = repo.save(&User {
        id: 0,
        name: "Alice".into(),
        email: "alice@example.com".into(),
        role: "user".into(),
        active: true,
    }).await?;
    println!("created user #{}", alice.id);

    // READ
    let found = repo.find_by_id(alice.id).await?.expect("user exists");
    println!("fetched: {} ({})", found.name, found.email);

    // UPDATE
    let promoted = repo.save(&User { role: "admin".into(), ..found }).await?;
    println!("role is now: {}", promoted.role);

    // EXISTS / COUNT
    println!("exists: {}", repo.exists_by_id(alice.id).await?);
    println!("total:  {}", repo.count().await?);

    // DELETE
    repo.delete_by_id(alice.id).await?;
    println!("deleted");

    Ok(())
}
```