# Rust + Sqlx + Sqlite

Untuk sekedar ingin membuat aplikasi sederhana, mungkin tidak memerlukan database server seperti PostgreSQL atau Mysql. Kita bisa memilih untuk menggunakan database sederhana seperti SQLite Database yang mana outputnya berupa file. Jadi kita bisa mengikutkan file tersebut ke dalam Git Repository sebagai bagian dari code.

## Setup Workspace

Di ekosistem Rust, untuk menginisialisasi directory workspace bisa menggunakan popular package manager yaitu menggunakan `Cargo`. Pada tulisan ini saya menggunakan `Cargo` dan `rustc` version `1.84.1`

```tsx
$ cargo --version
cargo 1.84.1 (66221abde 2024-11-19)

$ rustc --version
rustc 1.84.1 (e71f9a9a9 2025-01-27)
```

Selanjutnya dengan membuat workspace baru dengan nama `dbconn` sebagai contoh

```tsx
$ cargo new dbconn
$ cd dbconn
```

Agar code bisa terkoneksi ke sqlite, maka diperlukan `sqlx`. Jalankan command berikut untuk menambahkan `sqlx`. Pada saat tulisan ini dibuat, `sqlx` terbaru menggunakan version `v0.8.3`

```tsx
$ cargo add sqlx@0.8.3 --features runtime-tokio,sqlite
```

Pada command tersebut saya menambahkan option `--features`. Option ini digunakan agar cargo hanya meng-*compile* fitur-fitur yang diperlukan saja, sehingga binary yang dihasilkan akan lebih kecil dari pada meng-compile keseluruhan fitur yang mana tidak digunakan. Jika kita buka file `Cargo.toml` maka kurang lebih flag akan terlihat seperti ini

```tsx
[dependencies]
sqlx = { version = "0.8.3", features = ["runtime-tokio", "sqlite"] }
```

Selanjutnya karena async pada Rust bisa menggunakan beberapa package library, seperti `tokio` atau `async-std`. Pada tulisan ini, akan menggunakan `tokio` karena pada setup sqlx kita sudah menambahkan fitur yang di-enable adalah `runtime-tokio`.

```bash
$ cargo add tokio@1 --features full
```

Setelah semua package yang diperlukan ter-*download,* selanjutnya jalan `cargo check` guna memastikan semua dependencies terdownload.

```bash
$ cargo check
```

## Setup SQLite Database

Sebelum melakukan perubahan code pada [`main.rs`](http://main.rs), kita perlu setup database sqlite terlebih dahulu. Karena kita belum menggunakan migration tool apapun untuk meng-inisialisasi database, maka perlu melakukannya secara manual dengan langkah seperti berikut.

```bash
$ sqlite3 db.sql
sqlite> create table users (id int primary key, name varchar)
sqlite> insert into users (id, name) values (1, 'Sample User')
sqlite> select * from users;
1|Sample User
```

## Setup Connection and Query

Setelah selesai membuat sqlite database. Kita coba kembali ke directory `/tmp/dbconn`. Dimana pada directory ini terdapat file [`src/main.rs`](http://main.rs) yang berisi code start started Rust seperti ini.

```rust
fn main() {
   println!("Hello, world!")
}
```

Pada step ini kita perlu merevisi code tersebut agar dapat membuka koneksi ke database yang sudah dibuat sebelumnya. Seperti code di bawah ini.

```rust
use sqlx::sqlite::SqlitePoolOptions;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let mut pool = SqlitePoolOptions::new()
        .max_connections(5)
        .connect("sqlite:db.sql")
        .await?;

    Ok(())
}
```

Pada code tersebut terdapat sebuah variable dengan nama `pool`, dimana variable ini menyimpan instance dari `SqlitePoolOptions`. Pool ini merupakan kumpulan koneksi yang masih terhubung ke database yang mana memiliki maximal 5 koneksi. Selain itu untuk dapat melakukan query ke database, diperlukan `async/await`. Maka perlu menambahkan macro `tokio::main` pada function main.

Selanjutnya perlu membuat struct yang nantinya digunakan sebagai result dari `query_as` dari `sqlx`.

```rust
use sqlx::{ prelude::FromRow, sqlite::SqlitePoolOptions};

#[derive(Debug, FromRow)]
struct User {
   id: i64,
   name: String
}
```

Gunakan struct tersebut sebagai type annotation `Vec<User>` pada result query, sehingga hasil dari query akan di-*casting* ke dalam bentuk Struct tersebut seperti ini

```rust
async fn main() -> Result<(), sqlx::Error> {
	...
	let result = query_as::<_, User>(r#"select * from users"#)
		.fetch_all(&pool)
		.await?;
	...
}
```

Maka secara keseluruhan function main akan seperti berikut

```rust
use sqlx::{ prelude::FromRow, sqlite::SqlitePoolOptions, query_as };

#[derive(Debug, FromRow)]
struct User {
    id: i64,
    name: String
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let mut pool = SqlitePoolOptions::new()
        .max_connections(5)
        .connect("sqlite:db.sql")
        .await?;

    let result = query_as::<_, User>(r#"select * from users"#).fetch_all(&pool).await?;
    
    for item in result.iter() {
        println!("id: {}, name: {}", item.id, item.name);
    }

    Ok(())
}
```

Setelah semua tersetup, selanjutnya menjalankan `cargo run` untuk mendapatkan result.

```bash
$ cargo run
...
Compiling dbconn v0.1.0 (/tmp/dbconn)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s
     Running `target/debug/dbconn`
id: 1, name: Sample User
```

### Kesimpulan

Untuk membuka koneksi dari Rust ke SQLite database bisa dengan mudah menggunakan package `sqlx`.
