class: center, middle
# Full stack Rust
### Hyper Diesel Rust Rocket
Johannes Schriewer aka. Dunkelstern
---
class: center, middle
Wenn es fertig ist sieht das ganze so aus...
???
- Ein bisschen rostig für den Charme
- Flink
- Mit kleinem sidekick
- Mit ein bisschen glück kann man auch den Todesstern vernichten.
---
class: center, middle
Wenn man genau hinschaut ist sogar R2D2 mit an Board:
???
Und tausend anderen Modulen, hello javascript
---
class: center, middle
In Go sähe es dann so aus:
???
Ok ein bisschen Spaß muss sein...
---
# Übersicht
1. Wer bin ich
2. Rust im Backend
3. Rust für Datenbanken
4. Rust im Browser (WASM)
---
# Wer bin ich
- Backend-Entwickler bei anfema
- Normalerweise schreibe ich Python und Node.js
- Code seit 2003 in diversen Bereichen
- Enttäuscht von Swift
--
- Manche sagen ich bin ein irrer Bastler
--
- Wenn ich nicht Code bastel ich Elektronikgerümpel
---
# Rust im Backend
- Es gibt verschiedene Frameworks
--
- Iron
- Nickel
- Conduit
- Rocket
- Gotham
- ...
???
- Iron: Juni 2014, Aktuell kein Maintainer
- Nickel: Juni 2014, API ähnlich Express
- Conduit: Juni 2014, Wahrscheinlich tot
- Rocket: März 2016, Aktive Entwicklung
- Gotham: März 2017, Viele Beispiele, Aktive Entwicklung
--
- Das Benutzerfreundlichste ist [Rocket](https://rocket.rs)
--
(IMHO)
---
## Beispiel: Rocket
```rust
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[get("/hello//")]
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
fn main() {
rocket::ignite().mount("/", routes![hello]).launch();
}
```
???
- Wir brauchen Compiler Plugins
- ... und den Rocket Code-Generator für weniger Boilerplate
- Dann bauen wir uns einen Endpunkt
- Python Flask
- Return String, `format` ist ein macro
- Start mit einer einzelnen route
---
## JSON API Beispiel
### Model
```rust
#![feature(custom_derive)]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
use chrono::prelude::*;
#[derive(Serialize, Deserialize, Debug)]
pub struct Person {
pub id: i32,
pub name: String,
pub birthday: NaiveDateTime,
}
```
???
- Wieder ein neues rust-Feature
- Serde: Serializer-Deserializer
- Chrono: Date/Time lib
- Boilerplate lassen wir wieder generieren
- `Person` ist unser Model
---
### View
```rust
use rocket::response::Failure;
use rocket_contrib::Json;
#[get("/person/", format = "application/json")]
fn get_person(id: i32) -> Result, Failure> {
Ok(Json(Person {
id,
name: String::from("Max Musterman"),
birthday: Local::now().naive_local(),
}))
}
```
Die anderen HTTP Methoden funktionieren natürlich auch...
???
- JSON-Support laden
- Mock-Endpoint
- Statische Daten als Beispiel
- Reagiert nur auf `Accept: application/json`
---
#### Ergebnis
$ curl -v http://localhost:8080/person/1 \
-H 'Accept: application/json'
> GET /person/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.59.0
> Accept: application/json
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Server: Rocket
< Content-Length: 74
< Date: Sun, 15 Apr 2018 22:32:27 GMT
<
{
"id": 1,
"name": "Max Musterman",
"birthday":"2018-04-16T00:32:27.480509862"
}
???
- `Accept`-Header nicht vergessen sonst 404
- JSON formatiert zur besseren lesbarkeit
---
### Request Guards (aka Middleware)
```rust
#[get("/admin")]
fn admin_panel(admin: AdminUser) -> &'static str {
"Hello, administrator. This is the admin panel!"
}
#[get("/admin", rank = 2)]
fn admin_panel_user(user: User) -> &'static str {
"You must be an administrator to access this page."
}
#[get("/admin", rank = 3)]
fn admin_panel_redirect() -> Redirect {
Redirect::to("/login")
}
```
Rocket wählt automatisch denjenigen View der alle Request guards unterstützt (und den niedrigsten Rank hat)
???
- Middleware als opt-in
- Die Parameter-Typen implementieren `FromRequest`-Trait
- Nach Rank
- Request Guards müssen erfüllt werden sonst 404
- `format` im Decorator funktioniert ähnlich
---
# Rust für Datenbanken
- Auch hier wieder verschiedene Dinge:
--
- Postgres-Lib direkt
- Rustorm
- Diesel
- ...
???
- Postgres-Lib: Nicht direkt typesafe, bzw dynamisch
- Langsam
- Rustorm: Rewrite in progress, schlechte Doku
- Diesel: Dürftige Doku aber es geht voran
- Schneller als postgres-lib direkt
--
- Ich habe mich auf [Diesel](https://diesel.rs) eingeschossen weil es typesafe ist.
---
## Model definition (Diesel)
1. Table
```rust
table! {
person (id) {
id -> Integer,
name -> Text,
birthday -> Timestamp,
}
}
```
2. Model
```rust
#[derive(Serialize, Deserialize, Debug, Queryable, \
Insertable, Identifiable, AsChangeset)]
#[table_name = "person"]
pub struct Person {
...
```
???
- Migrations: SQL direkt
- `table!` definition lässt sich mit `diesel-cli` automatisieren
- Model wie vorher, mehr `derive`
- Boilerplate wird wieder generiert
---
## View (Diesel)
```rust
#[get("/persons")]
pub fn get_person_list(conn: DbConn)
-> QueryResult>>
{
person::table
.order(person::id.asc())
.load::(&*conn)
.map(|person| Json(person))
}
#[get("/person/")]
pub fn get_person(id: i32, conn: DbConn)
-> Result, Failure>
{
person::table
.find(id)
.first::(&*conn)
.map_err(|_| Failure(Status::NotFound))
.map(|person| Json(person))
}
```
???
- Sehr vereinfacht
- Kein DB-Connection Error-Handling
- `DbConn` kommt als Request-Guard
- Kaputte DB -> 404
- `map` konvertiert nur _Success_
- `map_err` konvertiert nur _Error_
---
## View (continued)
```rust
#[post("/person", data="")]
pub fn create_person(data: Json, conn: DbConn)
-> Result, Failure>
{
let rows_inserted = insert_into(person::table)
.values(&data.into_inner())
.execute(&*conn)
.unwrap();
if rows_inserted != 1 {
Err(Failure(Status::InternalServerError))
} else {
person::table
.order(person::id.desc())
.first::(&*conn)
.unwrap()
.map(|person| Json(person))
}
}
```
???
- Kurzes beispiel für nen DB-Save
- Der `DbConn`-Request-Guard erzeugt und committed transaction
- Nur deshalb ist der Query-Code safe
- Insert and Return geht noch nicht
- Serde und Diesel übernehmen das Decoding der `Person`
- Kein error handling beim insert: `unwrap()` crashed den worker -> 500
---
# Rust im Browser
1. _wasm-bindgen_ und Webpack, JS module in Rust
2. _yew_, React-style Framework, Everything Rust
???
- Beides nur kurz angerissen
- Alles sehr alpha
- Aber es funktioniert und wird weiterentwickelt
---
## WebAssembly Module für Javascript
### Rust Teil
```rust
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
```
???
- Wir brauchen `bindgen` und ne Menge **nightly** Kram
- Bindgen baut den Typ-Konverter von JS -> WASM
- Die Bindgen Macros bauen Typ-Konverter WASM -> Rust
- Externe Funktion `alert`, kann aber jede beliebige JS funktion sein
- Export von unserer `greet` funktion für JS
---
### Vorbereiten
```bash
$ rustup target add wasm32-unknown-unknown --toolchain nightly
$ cargo install wasm-bindgen-cli
```
### Compile
```bash
$ cargo +nightly build --target wasm32-unknown-unknown
$ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm \
--out-dir .
```
### Javascript
```js
const rust = import("./wasm_greet");
rust.then(m => m.greet("World!"));
```
???
- Erstmal brauchen wir tooling
- Dann compilen wir den Rust-Code
- Dann bauen wir den JS Glue-Code
- In Production sollte das dann Webpack machen
- Am Ende ist es einfach ein JS module
---
## Direkt in Rust rendern: Yew
https://github.com/DenisKolodin/yew
- JSX Style templates mittels _html!_ macro direkt im Rust code
- Application state management mittels Message passing
- ReactJS und elm waren die inspiration
- Eigener Virtual DOM
- Components und Fragments wie bereits bekannt
???
- Nur angerissen was theoretisch geht
- Projekt existiert seit Dezember 2017
- TODO-MVC geht aber schon
--
- **Very Alpha**
???
- Das ist als Warnung zu verstehen, Ich kenne ja die Javascript leute...
---
Dank [crates.io](https://crates.io) findet man aber noch viele Zusatzmodule
--
???
So wird dann aus unserem kleinen X-Wing ein großer Millenium Falcon.
Aber trotzdem:
> This is the ship that made the Kessel Run in fourteen parsecs?
---
Es gibt übrigens noch kein Crate namens _Solo_.
C3PO ist leider an den verkehrten Anwendungsfall gebunden, es wäre so schön gewesen wenn das ein I18N Crate wäre.
---
class: center, middle
# Danke für's Zuhören
Kommt gerne auf mich zu wenn ihr mehr wissen wollt!
[Are we web yet? http://www.arewewebyet.org/](http://www.arewewebyet.org/)
---
# Quellen
- [Rocket documentation / Getting started guide](https://rocket.rs/guide/)
- [Diesel documentation](http://diesel.rs/guides/getting-started/)
- [Mozilla Hacks Blog](https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/)
- [Wookiepedia](http://starwars.wikia.com/wiki/Main_Page)
# Kontakt
- hallo@dunkelstern.de
- Twitter: @dunkelstern
- Blog: blog.dunkelstern.de