| Crates.io | tiny-orm |
| lib.rs | tiny-orm |
| version | 0.5.1 |
| created_at | 2024-11-29 04:53:30.865857+00 |
| updated_at | 2025-01-27 04:00:39.120491+00 |
| description | A tiny ORM for CRUD operations built on top of SQLx QueryBuilder. |
| homepage | |
| repository | https://github.com/MattDelac/tiny_orm |
| max_upload_size | |
| id | 1465208 |
| size | 73,595 |
A minimal ORM for CRUD operations. Built on top of SQLx and with its QueryBuilder. Support Sqlite, Postgres and MySQL right off the bat. It has smart defaults and is also flexible.
This library is the one I wished I had when I built a project in Rust in production and did not want a heavy ORM.
You should consider using TinyORM if...
You should NOT use TinyORM, if...
cargo install tiny-orm -F postgres # sqlite or mysql
Or add this to your Cargo.toml:
[dependencies]
tiny-orm = {version = "0.5.1", features = ["postgres"] } # Choose between sqlite, mysql and postgres
Check Cargo.toml to see all the features available.
The goals of this library are
Why TinyORM over another one?
All the queries are built with the QueryBuilder
-- All the inputs are passed as database arguments
-- All the queries are compatible with Sqlite, Postgres and MySQL
Minimal set of dependencies (fast compile time)
-- SQLx
-- convert_case
-- regex
Intuitive with smart defaults and flexible
MSRV has been tested with cargo-msrv find --features sqlite. You can learn more about cargo-msrv on their website.
tiny_orm <= 0.4.0tiny_orm > 0.4.0This project is licensed under [MIT] - see the LICENSE file for details.
use sqlx::{FromRow, Row, types::chrono::{DateTime, Utc}};
use tiny_orm::Table;
#[derive(Debug, FromRow, Table, Clone)]
#[tiny_orm(all)]
struct Todo {
id: i32,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
description: String,
done: bool,
}
The code above would generate the following methods on the Todo object
impl Todo {
pub fn get_by_id(pool: &DbPool, id: &i32) -> sqlx::Result<Self> {
// Get a specific record for a given ID
// Use the `id` column by default
}
pub fn list_all(pool: &DbPool) -> sqlx::Result<Vec<Self>> {
// Get all the records
}
pub fn delete(&self, pool: &DbPool) -> sqlx::Result<()> {
// Delete the record in the database
}
pub fn create(&self, pool: &DbPool) -> sqlx::Result<i32> {
// Create the Todo object as a record in
// the database and returns the primary key of the record created.
}
pub fn update(&self, pool: &DbPool) -> sqlx::Result<()> {
// Update the record in the database with the values
// currently part of the Todo object
}
}
More examples can be found in the examples directory.
The Table macro comes with a few options to give flexibility to the user
MyStruct would have my_struct as a default table_name.deleted_at filter when querying the data. When using the delete method, it will set the deleted_at column to the current timestamp.
Default false.Self. Useful when creating or updating records with partial information.
Default is Self which corresponds to the current Strut.Note: only cannot be used with exclude nor add cannot be used together.
By convention, if a struct name
New then it will automatically use the following arguments
#[tiny_orm(table_name = "table_name_from_return_object", return_object = "NameWithoutNewAsPrefix", only = "create")]Update then it will automatically use the following arguments
#[tiny_orm(table_name = "table_name_from_return_object", return_object = "NameWithoutUpdateAsPrefix", only = "update")]#[tiny_orm(table_name = "table_name", return_object = "Self", exclude = "create,update")]#[derive(Debug, FromRow, Table, Clone)]
// #[tiny_orm(table_name = "todo", return_object = "Self", exclude = "create,update")]
struct Todo {
id: i32,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
description: String,
done: bool,
}
#[derive(Debug, FromRow, Table, Clone)]
// Because the struct name starts with "New", it would automatically use the following options
// #[tiny_orm(table_name = "todo", return_object = "Todo", only = "create")]
struct NewTodo {
description: String,
}
#[derive(Debug, FromRow, Table, Clone)]
// Because the struct name starts with "Update", it would automatically use the following options
// #[tiny_orm(table_name = "todo", return_object = "Todo", only = "update")]
struct UpdateTodo {
id: i32,
done: bool
}
The above takes the assumption that some columns can be nullable and others are auto generated (eg an ID column that would be auto increment). Thus it would generate the following
impl NewTodo {
pub fn create(&self, pool: &DbPool) -> sqlx::Result<Todo> {
// Use the NewTodo object to create a record
// in the database and return the record created.
// This would not be supported for MySQL since
// there is a need to know the primary key (auto increment or not)
// to either return the value or use `last_insert_id()`
}
}
impl UpdateTodo {
pub fn update(&self, pool: &DbPool) -> sqlx::Result<Todo> {
// Update the Todo object with partial information based
// on the id (default primary_key).
// Because `return_type` is specified, it will return the whole record in the database.
// If `return_type` was not specified, it would simply return nothing `()`.
// For MySQL, it would always return nothing `()` as it
// cannot return a record after an update.
}
}
#[derive(Debug, FromRow, Table, Clone)]
#[tiny_orm(soft_deletion)]
struct Todo {
id: i32,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
deleted_at: DateTime<Utc>,
description: String,
done: bool,
}
#[derive(Debug, FromRow, Table, Clone)]
#[tiny_orm(soft_deletion)]
struct UpdateTodo {
id: i32,
done: bool
}
get_by_id()). If not specified, it will default to id if part of the struct.create() method since it will be auto generated by the database itself.Note: MySQL only supports "auto increment" in that case. It does not support returning the default value of a primary key like a UUID.
Example
#[derive(Debug, FromRow, Table, Clone)]
struct Todo {
#[tiny_orm(primary_key)]
custom_pk: Uuid,
description: String,
done: bool
}
use tiny_orm::{Table, SetOption};
#[derive(Debug, FromRow, Table, Clone)]
struct Todo {
#[tiny_orm(primary_key(auto))]
id: i64,
description: SetOption<String>,
done: SetOption<bool>
}
Thus it would generate the following
impl Todo {
pub fn create(&self, pool: &DbPool) -> sqlx::Result<Uuid> { // or i64 depending on the example
// Create the Todo object as a record in
// the database and returns the created record.
}
}
SetOptionSetOption is an Enum that behaves similarly to Option. The main difference is that Tiny ORM will automatically exclude the fields once they are Unset.
The goal is to not always push all the fields during update or create operations. This would avoid some potential data race condition if the local struct is out of date with what the database has.
You can check tiny-orm-model/src/lib.rs but SetOption implement most of the Traits needed to automatically be used with Sqlx (Encode, Decode) as well as useful methods like .inner(), .is_set() & .is_not_set().
You can check the sqlite-setoption example to see it in action.
In 0.2.0, the API was stabilized with the fact that
update() and delete() methods now always return an empty Ok response sqlx::Result<()>create() method always returns the primary key except when the return type is of another object (eg: NewTodo has a return_object of Todo)Goal of TinyORM is to stay tiny. That being said there are still a few things I would like to have
last_return_id() function in the case (eg with UUID).created_at / updated_at column based on some default value when needed.Utc.now() on the update() method for the updated_at if the field is of type DateTime<Utc>SetOption<DateTime<Utc>> then it would only put the field as part of the query if the value is Set(value). It would skip it if it has the value NotSet.One PR with the following
make release INCREMENT=patch
then add the tag and push it to main
git tag -a v0.2.0 -m "Release v0.2.0"
git push origin main --tags