Update state guide for 'rocket_db_pools'.

This commit is contained in:
Sergio Benitez 2022-05-09 04:56:07 -05:00
parent 58b96b8e94
commit e1a19054d0
1 changed files with 90 additions and 129 deletions

View File

@ -216,147 +216,108 @@ request-local state to implement request timing.
## Databases
Rocket includes built-in, ORM-agnostic support for databases. In particular,
Rocket provides a procedural macro that allows you to easily connect your Rocket
application to databases through connection pools. A _database connection pool_
is a data structure that maintains active database connections for later use in
the application. This implementation of connection pooling support is based on
[`r2d2`] and exposes connections through request guards. Databases are
individually configured through Rocket's regular configuration mechanisms: a
`Rocket.toml` file, environment variables, or procedurally.
Rocket includes built-in, ORM-agnostic support for databases via
[`rocket_db_pools`]. The library simplifies accessing one or more databases via
connection pools: data structures that maintain active database connections for
use in the application. Database configuration occurs via Rocket's regular
[configuration](../configuration) mechanisms.
Connecting your Rocket application to a database using this library occurs in
three simple steps:
Connecting your Rocket application to a database using `rocket_db_pools` happens
in three simple steps:
1. Configure the databases in `Rocket.toml`.
2. Associate a request guard type and fairing with each database.
3. Use the request guard to retrieve and use a connection in a handler.
1. Choose your database(s) from the [supported database driver list]. Add
`rocket_db_pools` as a dependency in `Cargo.toml` with respective database
driver feature(s) enabled:
Presently, Rocket provides built-in support for the following databases:
```toml
[dependencies.rocket_db_pools]
version = "0.1.0-rc.2"
features = ["sqlx_sqlite"]
```
<!-- Note: Keep this table in sync with contrib/sync_db_pools/src/lib.rs -->
| Kind | Driver | Version | `Poolable` Type | Feature |
|----------|-----------------------|-----------|--------------------------------|------------------------|
| MySQL | [Diesel] | `1` | [`diesel::MysqlConnection`] | `diesel_mysql_pool` |
| Postgres | [Diesel] | `1` | [`diesel::PgConnection`] | `diesel_postgres_pool` |
| Postgres | [Rust-Postgres] | `0.19` | [`postgres::Client`] | `postgres_pool` |
| Sqlite | [Diesel] | `1` | [`diesel::SqliteConnection`] | `diesel_sqlite_pool` |
| Sqlite | [`Rusqlite`] | `0.24` | [`rusqlite::Connection`] | `sqlite_pool` |
| Memcache | [`memcache`] | `0.15` | [`memcache::Client`] | `memcache_pool` |
2. Choose a name for your database, here `sqlite_logs`. [Configure] _at least_
a URL for the database under `databases.$name` (here, in `Rocket.toml`),
where `$name` is your choice of database name:
[`r2d2`]: https://crates.io/crates/r2d2
[Diesel]: https://diesel.rs
[`rusqlite::Connection`]: https://docs.rs/rusqlite/0.23.0/rusqlite/struct.Connection.html
[`diesel::SqliteConnection`]: https://docs.diesel.rs/diesel/prelude/struct.SqliteConnection.html
[`postgres::Client`]: https://docs.rs/postgres/0.19/postgres/struct.Client.html
[`diesel::PgConnection`]: https://docs.diesel.rs/diesel/pg/struct.PgConnection.html
[`diesel::MysqlConnection`]: https://docs.diesel.rs/diesel/mysql/struct.MysqlConnection.html
[`Rusqlite`]: https://github.com/jgallagher/rusqlite
[Rust-Postgres]: https://github.com/sfackler/rust-postgres
[`diesel::PgConnection`]: https://docs.diesel.rs/diesel/pg/struct.PgConnection.html
[`memcache`]: https://github.com/aisk/rust-memcache
[`memcache::Client`]: https://docs.rs/memcache/0.15/memcache/struct.Client.html
```toml
[default.databases.sqlite_logs]
url = "/path/to/database.sqlite"
```
### Usage
3. [Derive `Database`] for a unit `Type` (`Logs` here) which wraps the selected
driver's `Pool` type from the [supported database driver list]. Decorated the
struct with `#[database("$name")]` with the `$name` from `2.`. Attach
`$Type::init()` to your application's `Rocket` to initialize the database
pool and use [`Connection<$Type>`] as a request guard to retrieve an active
database connection:
To connect your Rocket application to a given database, first identify the
"Kind" and "Driver" in the table that matches your environment. The feature
corresponding to your database type must be enabled. This is the feature
identified in the "Feature" column. For instance, for Diesel-based SQLite
databases, you'd write in `Cargo.toml`:
```rust
#[macro_use] extern crate rocket;
use rocket_db_pools::{Database, Connection};
use rocket_db_pools::sqlx::{self, Row};
#[derive(Database)]
#[database("sqlite_logs")]
struct Logs(sqlx::SqlitePool);
#[get("/<id>")]
async fn read(mut db: Connection<Logs>, id: i64) -> Option<String> {
sqlx::query("SELECT content FROM logs WHERE id = ?").bind(id)
.fetch_one(&mut *db).await
.and_then(|r| Ok(r.try_get(0)?))
.ok()
}
#[launch]
fn rocket() -> _ {
rocket::build().attach(Logs::init()).mount("/", routes![read])
}
```
For complete usage details, see [`rocket_db_pools`].
[`rocket_db_pools`]: @api/rocket_db_pools/index.html
[supported database driver list]: @api/rocket_db_pools/index.html#supported-drivers
[database driver features]: @api/rocket_db_pools/index.html#supported-drivers
[`Pool`]: @api/rocket_db_pools/index.html#supported-drivers
[Configure]: @api/rocket_db_pools/index.html#configuration
[Derive `Database`]: @api/rocket_db_pools/derive.Database.html
[`Connection<$Type>`]: @api/rocket_db_pools/struct.Connection.html
### Driver Features
Only the minimal features for each driver crate are enabled by
`rocket_db_pools`. To use additional driver functionality exposed via its
crate's features, you'll need to depend on the crate directly with those
features enabled in `Cargo.toml`:
```toml
[dependencies.rocket_sync_db_pools]
version = "0.1.0-rc.1"
[dependencies.sqlx]
version = "0.5"
default-features = false
features = ["diesel_sqlite_pool"]
features = ["macros", "offline", "migrate"]
[dependencies.rocket_db_pools]
version = "0.1.0-rc.2"
features = ["sqlx_sqlite"]
```
Then, in `Rocket.toml` or the equivalent via environment variables, configure
the URL for the database in the `databases` table:
### Synchronous ORMs
```toml
[global.databases]
sqlite_logs = { url = "/path/to/database.sqlite" }
```
In your application's source code, create a unit-like struct with one internal
type. This type should be the type listed in the "`Poolable` Type" column. Then
decorate the type with the `#[database]` attribute, providing the name of the
database that you configured in the previous step as the only parameter. You
will need to either add `#[macro_use] extern crate rocket_sync_db_pools` to the
crate root or have a `use rocket_sync_db_pools::database` in scope, otherwise
the `database` attribute will not be available. Finally, attach the fairing
returned by `YourType::fairing()`, which was generated by the `#[database]`
attribute:
```rust
# #[macro_use] extern crate rocket;
use rocket_sync_db_pools::{diesel, database};
#[database("sqlite_logs")]
struct LogsDbConn(diesel::SqliteConnection);
#[launch]
fn rocket() -> _ {
rocket::build().attach(LogsDbConn::fairing())
}
```
That's it! Whenever a connection to the database is needed, use your type as a
request guard. The database can be accessed by calling the `run` method:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# use rocket_sync_db_pools::{diesel, database};
# #[database("sqlite_logs")]
# struct LogsDbConn(diesel::SqliteConnection);
# type Logs = ();
#[get("/logs/<id>")]
async fn get_logs(conn: LogsDbConn, id: usize) -> Logs {
# /*
conn.run(|c| logs::filter(id.eq(log_id)).load(c)).await
# */
}
```
! note The above examples uses [Diesel] with some fictional `Logs` type.
The example above contains the use of a `Logs` type that is application
specific and not built into Rocket. It also uses [Diesel]'s query-building
syntax. Rocket does not provide an ORM. It is up to you to decide how to model
your application's data.
<!---->
! note: Rocket wraps synchronous databases in an `async` API.
The database engines supported by `#[database]` are *synchronous*. Normally,
using such a database would block the thread of execution. To prevent this,
the `run()` function automatically uses a thread pool so that database access
does not interfere with other in-flight requests. See
[Multitasking](../overview/#multitasking) for more information on why this is
necessary.
If your application uses features of a database engine that are not available
by default, for example support for `chrono` or `uuid`, you may enable those
features by adding them in `Cargo.toml` like so:
```toml
[dependencies]
postgres = { version = "0.15", features = ["with-chrono"] }
```
For more on Rocket's sanctioned database support, see the
[`rocket_sync_db_pools`] library documentation. For examples of CRUD-like "blog"
JSON APIs backed by a SQLite database driven by each of `sqlx`, `diesel`, and
`rusqlite` with migrations run automatically for the former two drivers and
Rocket's database support use for the latter two drivers, see the [databases
example](@example/databases).
While [`rocket_db_pools`] provides support for `async` ORMs and should thus be
the preferred solution, Rocket also provides support for synchronous, blocking
ORMs like [Diesel] via the [`rocket_sync_db_pools`] library, which you may wish
to explore. Usage is similar, but not identical, to `rocket_db_pools`. See the
crate docs for complete usage details.
[`rocket_sync_db_pools`]: @api/rocket_sync_db_pools/index.html
[diesel]: https://diesel.rs/
### Examples
For examples of CRUD-like "blog" JSON APIs backed by a SQLite database driven by
each of `sqlx`, `diesel`, and `rusqlite`, with migrations run automatically for
the former two drivers, see the [databases example](@example/databases). The
`sqlx` example uses `rocket_db_pools` while the `diesel` and `rusqlite` examples
use `rocket_sync_db_pools`.