Construyendo

APIs Web de alto rendimiento con Rust y Rocket

@sebasmagri - Enero de 2017

Agenda

  • ¿APIs?
  • ¿Qué necesitas saber de Rust para trabajar con Rocket?
  • Breve introducción a Rocket
  • Manos a la obra
  • Referencias adicionales

¿Qué es un API Web?

Conceptos Básicos

API

Application Programmer Interface

+

Web

Tecnologías y Protocolos usables en un ámbito específico

Algunos Patrones o Estilos

  • SOA
  • RPC
  • Como Salga.
  • REST

REST

  • Recursos
    • URIs o Identificadores
    • Representaciones
  • Verbos HTTP

Herramientas para el desarrollo Web con Rust

  • Frameworks
    • Iron, Nickel, Pencil, Rocket
  • Soporte de bases de datos
  • Formatos y Tipos de Contenido

 

http://www.arewewebyet.org/

¿Qué tanto Rust debo saber para hacer APIs Web con Rocket?

Principios Fundamentales de Rust

Seguridad

Concurrencia

Velocidad

Structs

Enums

Manejo de datos

struct LoadAvg {
    last1: f64,
    last5: f64,
    last15: f64
}

...

let load_avg = LoadAvg {
    last1: 0.36,
    last5: 0.71,
    last15: 1.1
}

...

println!("{}", load_avg.last1)
enum Gender {
    Male,
    Female,
    Other
}
...
struct Person {
    name: String,
    age: i32,
    gender: Gender
}
...
let p = Person {
    name: "Sebastián".to_string(),
    age: 15,
    gender: Gender::Male
}

Descripción de un tipo

Un tipo con opciones

Seguridad

Ownership Borrowing Lifetimes

Herramientas para el manejo seguro de memoria, aún en ambientes altamente concurrentes.

Ownership y Lifetimes

Manejo de propiedad de memoria

  1. Cualquier valor se puede usar solo una vez. En su primer uso es movido a su nueva ubicación y ya no se puede usar desde la anterior.
  2. Todo valor es destruido al terminar su scope.
  3. Un bloque de código puede producir un valor usable por un bloque superior, como valor de retorno.
  4. Todo valor tiene un tiempo de vida (lifetime) asociado, el cual define el alcance del mismo (scope).

Borrowing

AKA: peleando con el borrow checker

  1. Puede existir una y solo una referencia mutable a una variable al mismo tiempo.
  2. Pueden existir tantas referencias inmutables a una variable como sean necesarias.
  3. No se pueden mezclar referencias mutables e inmutables a una misma variable.

Rocket

Un framework de desarrollo Web para Rust, enfocado en la simplicidad, el rendimiento, la flexibilidad, y el manejo seguro de tipos de datos.

https://rocket.rs/

Routes

Rocket hace uso de atributos de ruta para definir qué funciones atienden una solicitud específica.

Estos atributos permiten definir el método, la ruta y los tipos de datos de solicitud y de respuesta.

#[get("/")]
fn home() -> String { ... }

#[post("/noticias", data = "<entry>")]
fn new(entry: Entry) -> String { ... }

#[head("/noticias")]
fn head() -> String { ... }

#[get("/noticias")]
fn entries() -> String { ... }

#[get("/noticias/<id>")]
fn entries(id: i32) -> String { ... }

#[put("/noticias/<id>", data = "<entry>")]
fn update(entry: Entry) -> String { ... }

#[patch("/noticias/<id>", data = "<entry>")]
fn update_partial(id: i32, entry: Entry) -> ...

#[route(OPTIONS, "/noticias")]
fn options() -> String { ... }

Manejo de URLs

Handlers

Un handler se encarga de manejar una solicitud para la que el router tiene una coincidencia en las rutas declaradas y de emitir una respuesta después de procesar los datos de entrada.

#[get("/")]
fn hello() -> String {
    "Hello World".to_string()
}

// Parámetros dinámicos por URL
#[get("/<name>")]
fn home(name: &str) -> String {
    format!("Hello {}!", name)
}

// Parámetros en el cuerpo de la solicitud
#[post("/login", data = "<user_form>")]
fn login(user_form: Form<UserLogin>) -> String {
   // Use `user_form`, return a String.
}

// ## Request Guards
// Permiten establecer requerimientos
// que debe cubrir una solicitud
#[get("/sensitive")]
fn sensitive(key: APIKey) -> &'static str { ... }

Manejo de solicitudes

Responders

Responder es un trait provisto por Rocket que se encarga de convertir un tipo de datos en una respuesta.

Cualquier tipo de datos devuelto por un handler debe implementar el trait Responder.

struct SystemLoad {
    last1: f32,
    last5: f32,
    last15: f32
}

impl<'r, R: Responder<'r>> Responder<'r> for SystemLoad { ... }

#[get("/")]
fn hello() -> T {
    "Hello World".to_string()
}

Procesamiento de respuestas

Iniciando la aplicación

El proceso de arranque permite hacer el montaje de los manejadores de solicitudes (o de errores) en sus rutas raíz e iniciar un servidor Web para la aplicación.

rocket::ignite()
   .mount("/base", routes![index, another])
   .launch()

...

🚀  Rocket has launched from http://localhost:8000...

A lo práctico...

Algunas referencias finales

- Repositorio de este taller: https://github.com/sebasmagri/rocket-loadavg-api

- El libro de Rust: https://doc.rust-lang.org/stable/book/

- Documentación de crates: https://docs.rs/

- La guía oficial de Rocket: https://rocket.rs/guide

- Los ejemplos oficiales: https://github.com/SergioBenitez/Rocket

- El canal de IRC de Rocket: #rocket@irc.mozilla.org

(también en Matrix)

¡Gracias!

Construyendo servicios web de alto rendimiento con Rust y Rocket

By sebasmagri

Construyendo servicios web de alto rendimiento con Rust y Rocket

  • 1,715