From 56fd0881634aef9fbab09b1f09dc1f3336c178de Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 18 Dec 2017 19:38:16 -0800 Subject: [PATCH] added database integration guide section --- examples/diesel/src/main.rs | 2 +- guide/src/SUMMARY.md | 1 + guide/src/qs_14.md | 127 ++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 guide/src/qs_14.md diff --git a/examples/diesel/src/main.rs b/examples/diesel/src/main.rs index 07e99e0dd..9e03873cd 100644 --- a/examples/diesel/src/main.rs +++ b/examples/diesel/src/main.rs @@ -37,7 +37,7 @@ fn index(req: HttpRequest) -> Box> req.state().db.call_fut(CreateUser{name: name.to_owned()}) .and_then(|res| { match res { - Ok(person) => ok(httpcodes::HTTPOk.build().json(person).unwrap()), + Ok(user) => ok(httpcodes::HTTPOk.build().json(user).unwrap()), Err(_) => ok(httpcodes::HTTPInternalServerError.response()) } }) diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index e260000a7..275392118 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -12,3 +12,4 @@ - [Middlewares](./qs_10.md) - [Static file handling](./qs_12.md) - [HTTP/2](./qs_13.md) +- [Database integration](./qs_14.md) diff --git a/guide/src/qs_14.md b/guide/src/qs_14.md new file mode 100644 index 000000000..38ea411cf --- /dev/null +++ b/guide/src/qs_14.md @@ -0,0 +1,127 @@ +# Database integration + +## Diesel + +At the moment of 1.0 release Diesel does not support asynchronous operations. +But it possible to use `actix` synchronous actor as an db interface api. +Multipl sync actors could be started, in this case all of this actor +process messages from same queu (sync actors actually work mpmc mode). + +Let's create simple db api that can insert new user row into sqlite table. +We need to define sync actor and connection that this actor will use. Same approach +could used for other databases: + +```rust,ignore +use actix::prelude::*;* + +struct DbExecutor(SqliteConnection); + +impl Actor for DbExecutor { + type Context = SyncContext; +} +``` + +This is definition of our actor. Now we need to define *create user* message. + +```rust,ignore +struct CreateUser { + name: String, +} + +impl ResponseType for CreateUser { + type Item = models::User; + type Error = Error; +} +``` + +We can send `CreateUser` message to `DbExecutor` actor, and as result we can get +`User` model. Now we need to define actual handler for this message. + +```rust,ignore +impl Handler for DbExecutor { + fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Response + { + use self::schema::users::dsl::*; + + // Create insertion model + let uuid = format!("{}", uuid::Uuid::new_v4()); + let new_user = models::NewUser { + id: &uuid, + name: &msg.name, + }; + + // normal diesl operations + diesel::insert_into(users) + .values(&new_user) + .execute(&self.0) + .expect("Error inserting person"); + + let mut items = users + .filter(id.eq(&uuid)) + .load::(&self.0) + .expect("Error loading person"); + + Self::reply(items.pop().unwrap()) + } +} +``` + +That is it. Now we can use *DbExecutor* actor from any http handler or middleware. +All we need is to start *DbExecutor* actors and store address in state where http endpoint +can access it. + + +```rust,ignore +/// This is state where we sill store *DbExecutor* address. +struct State { + db: SyncAddress, +} + +fn main() { + let sys = actix::System::new("diesel-example"); + + // Start 3 db executors + let addr = SyncArbiter::start(3, || { + DbExecutor(SqliteConnection::establish("test.db").unwrap()) + }); + + // Start http server + HttpServer::new(move || { + Application::with_state(State{db: addr.clone()}) + .resource("/{name}", |r| r.method(Method::GET).a(index))}) + .bind("127.0.0.1:8080").unwrap() + .start().unwrap(); + + println!("Started http server: 127.0.0.1:8080"); + let _ = sys.run(); +} +``` + +And finally we can use this handler function. We get message response +asynchronously, so handler needs to return future object, also `Route::a()` needs to be +used for async handler registration. + + +```rust,ignore +/// Async handler +fn index(req: HttpRequest) -> Box> { + let name = &req.match_info()["name"]; + + Box::new( + // Send message to `DbExecutor` actor + req.state().db.call_fut(CreateUser{name: name.to_owned()}) + .and_then(|res| { + match res { + Ok(user) => ok(HTTPOk.build().json(user)), + Err(_) => ok(HTTPInternalServerError.response()) + } + }) + .map_err(|e| error::ErrorInternalServerError(e).into())) +} +``` + +Full example is available in +[examples repository](https://github.com/actix/actix-web/tree/master/examples/diesel/). + +More information on sync actors could be found in +[actix documentation](https://docs.rs/actix/0.3.3/actix/sync/index.html).