#models #crud #interaction #simplify

naphtha

Universal database connection layer for your application

8 releases (5 breaking)

0.5.0 Feb 27, 2022
0.4.1 Nov 22, 2021
0.3.1 Sep 20, 2021
0.2.0 Sep 11, 2021
0.0.0 Apr 14, 2021

#1341 in Database interfaces

MIT/Apache

24KB
344 lines

naphtha

This crate is currently in progress. Visit the docs.rs documentation page for more information.


lib.rs:

This library provides several traits in order to make database access a lot easier. In addition when using naphtha, it is possible to change the database that is used for specific models in your application without the requirement of changing any code.

It implements the most common operations on a database like insert, update and remove for you, while also providing the ability to send custom queries to the database. In addition to that, when using the barrel-XXX features, you can write your SQL migrations and use them in your application during runtime. See the examples below.

Features overview

  • Most common function implementations insert, update, remove for your models.
  • Custom transactions provided by the custom function
  • DatabaseUpdateHandler enables you to change the models values before and after the update transaction to the database.
  • Change database on specific model in your application without the need to change your code.
  • Possibility to query a model from the database by using one of its member.
  • Integrated [barrel] for writing your SQL migrations and the possibility to apply them during runtime.
  • Thread safe handling of the database connection.

Supported databases

  • SQlite3 (using diesel under the hood).
  • MySQL (using diesel under the hood).
  • PostgreSQL (using diesel under the hood).

Examples

In this chapter, minimal usages are shown. Please have a look at the examples in the repository for more and detailed use.

Connecting to a database

use naphtha::{DatabaseConnection, DatabaseConnect};
// This is the only line required to be changed to switch database types.
type DbBackend = diesel::SqliteConnection;
let db: DatabaseConnection<DbBackend> = DatabaseConnection::connect(":memory:").unwrap();
// do some database work

Defining a model and use database connection

To create a model and its database integration, the following code is required.

Note that this is an excerpt, see the examples folder in the repository for a full working example.

#[model(table_name = "persons")]
pub struct Person {
    id: i32,
    pub description: Option<String>,
    pub updated_at: NaiveDateTime,
}

pub mod schema {
    table! {
        persons (id) {
            id -> Int4,
            description -> Nullable<Varchar>,
            updated_at -> Timestamp,
        }
    }
}

impl DatabaseModel for Person {
    type PrimaryKey = i32;
    fn primary_key(&self) -> Self::PrimaryKey {
        self.id
    }

    fn set_primary_key(&mut self, value: &Self::PrimaryKey) {
        self.id = *value;
    }

    fn default_primary_key() -> Self::PrimaryKey {
        0
    }

    fn table_name() -> &'static str {
        "persons"
    }
}

// Define your custom changes to the model before and after the transactions.
impl<T> naphtha::DatabaseUpdateHandler<T> for Person {}
impl<T> naphtha::DatabaseRemoveHandler<T> for Person {}
impl<T> naphtha::DatabaseInsertHandler<T> for Person {}

// This implements your database migration functions.
impl DatabaseSqlMigration for Person {
    fn migration_up(migration: &mut Migration) {
        use naphtha::DatabaseModel;
        migration.create_table_if_not_exists(Self::table_name(), |t| {
            t.add_column("id", types::primary());
            t.add_column("description", types::text().nullable(true));
            t.add_column("updated_at", types::custom("timestamp"));
        });
    }

    fn migration_down(migration: &mut Migration) {
        use naphtha::DatabaseModel;
        migration.drop_table_if_exists(Self::table_name());
    }
}

fn main() {
    use naphtha::{DatabaseConnection, DatabaseConnect};
    let db = DatabaseConnection::connect(":memory:").unwrap();
    // p is to be mutable because the insert function updates the id member
    // to the one given by the database.
    let mut p = Person {
        id: Person::default_primary_key(),
        description: Some("The new person is registered".into()),
    };
    p.insert(&db);
    // id member is set to the correct number given by the database.

    // do a custom query to the database
    db.custom::<diesel::result::QueryResult::<Person>, _>(|c: &DbBackend| {
        use schema::{persons, persons::dsl::*};
        persons.filter(id.eq(1)).first(c)
    });

    p.remove(&db);
    // p not available anymore in the database
}

Dependencies

~4–8MB
~154K SLoC