1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 17:52:40 +01:00
actix-extras/guide/src/qs_14.md

126 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.
2017-12-19 05:00:57 +01:00
But it possible to use `actix` synchronous actor system as a db interface api.
2017-12-19 05:03:42 +01:00
Technically sync actors are worker style actors, multiple of them
can be run in parallel and process messages from same queue (sync actors work in mpmc mode).
Let's create simple db api that can insert new user row into sqlite table.
2017-12-26 16:58:21 +01:00
We have to define sync actor and connection that this actor will use. Same approach
could be used for other databases.
```rust,ignore
use actix::prelude::*;*
struct DbExecutor(SqliteConnection);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
```
2017-12-19 05:00:57 +01:00
This is definition of our actor. Now we need to define *create user* message and response.
```rust,ignore
2017-12-26 16:58:21 +01:00
#[derive(Message)]
#[rtype(User, Error)]
struct CreateUser {
name: String,
}
```
2017-12-27 06:07:51 +01:00
We can send `CreateUser` message to `DbExecutor` actor, and as result we get
2017-12-19 05:00:57 +01:00
`User` model. Now we need to define actual handler implementation for this message.
```rust,ignore
impl Handler<CreateUser> for DbExecutor {
2018-01-13 20:17:48 +01:00
type Result = Result<User, Error>
2017-12-19 05:00:57 +01:00
2018-01-13 20:17:48 +01:00
fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result
{
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");
2018-01-13 20:17:48 +01:00
Ok(items.pop().unwrap())
}
}
```
That is it. Now we can use *DbExecutor* actor from any http handler or middleware.
2017-12-26 16:58:21 +01:00
All we need is to start *DbExecutor* actors and store address in a state where http handler
can access it.
```rust,ignore
2017-12-27 06:07:51 +01:00
/// This is state where we will store *DbExecutor* address.
struct State {
2018-02-17 22:33:38 +01:00
db: Addr<Syn, DbExecutor>,
}
fn main() {
let sys = actix::System::new("diesel-example");
2018-01-13 20:17:48 +01:00
// Start 3 parallel 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();
}
```
2018-01-13 20:17:48 +01:00
And finally we can use address in a request handler. 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"];
2017-12-21 06:06:04 +01:00
// Send message to `DbExecutor` actor
2018-02-17 22:33:38 +01:00
req.state().db.send(CreateUser{name: name.to_owned()})
2017-12-21 06:06:04 +01:00
.from_err()
.and_then(|res| {
match res {
Ok(user) => Ok(httpcodes::HTTPOk.build().json(user)?),
2018-01-01 02:26:32 +01:00
Err(_) => Ok(httpcodes::HTTPInternalServerError.into())
2017-12-21 06:06:04 +01:00
}
})
.responder()
}
```
Full example is available in
2017-12-19 19:10:03 +01:00
[examples directory](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.5.0/actix/sync/index.html).