1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-25 08:22:43 +01:00
actix-extras/guide/src/qs_14.md

128 lines
3.6 KiB
Markdown
Raw Normal View History

# 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<Self>;
}
```
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<CreateUser> for DbExecutor {
fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Response<Self, CreateUser>
{
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::<models::User>(&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<DbExecutor>,
}
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<State>) -> Box<Future<Item=HttpResponse, Error=Error>> {
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).