Índice

APIs web con nyx-serve

Que es nyx-serve?

nyx-serve es un framework HTTP para Nyx que te ofrece enrutamiento, middleware, manejo de JSON y sesiones listo para usar. Sigue la misma arquitectura del caso de estudio de nyx-kv: un pool de hilos de trabajo procesando solicitudes desde un canal compartido.

App, Request y Response

Toda aplicacion nyx-serve comienza con tres structs:

struct App {
    routes: Array,
    middlewares: Array,
    static_dir: String,
}

struct Request {
    method: String,
    path: String,
    query: Map,
    headers_flat: Array,
    body: String,
    form: Map,
    cookies: Map,
    params: Map,
}

struct Response {
    status: int,
    headers_flat: Array,
    body: String,
}

Crea una app, registra rutas y comienza a servir:

import "std/web"

fn handle_index(req: Request) -> Response {
    return response_html(200, "<h1>Hello!</h1>")
}

fn main() {
    let app: App = app_new()
    app_get(app, "/", handle_index)
    serve_app(app, 3000, 64)
}

Enrutamiento

Registra handlers para cada metodo HTTP:

app_get(app, "/users", list_users)
app_post(app, "/users", create_user)
app_put(app, "/users/{id}", update_user)
app_delete(app, "/users/{id}", delete_user)

Los patrones de ruta soportan parametros con nombre ({id}) y comodines (/static/*). Los parametros con nombre son accesibles via req.params:

fn get_user(req: Request) -> Response {
    let id: String = req.params.get("id")
    return response_text(200, "User: " + id)
}

Middleware

Las funciones de middleware se ejecutan antes de tus handlers de ruta. Registralas con app_use:

let app: App = app_new()
app_use(app, mw_logging)    // registra cada solicitud
app_use(app, mw_cors)       // maneja preflight CORS

nyx-serve incluye dos funciones de middleware integradas:

Configura CORS antes de registrar el middleware:

cors_configure("*", "GET,POST,PUT,DELETE", "Content-Type,Authorization")

Un middleware devuelve una Response con status: 0 para continuar la cadena, o un estado distinto de cero para cortocircuitar (ej. devolver 403 para solicitudes no autorizadas).

APIs JSON

Parsea el cuerpo de una solicitud JSON a un Map:

fn create_user(req: Request) -> Response {
    let data: Map = req_json(req)
    let name: String = data.get("name")
    // ... crear usuario ...
    return response_json(201, "{\"ok\":true}")
}

Construye una respuesta JSON desde un Map:

fn get_status(req: Request) -> Response {
    let data: Map = Map.new()
    data.insert("status", "ok")
    data.insert("version", "1.0")
    return response_json_map(200, data)
}

Sesiones

Las sesiones estan respaldadas por cookies y almacenadas en nyx-kv via RESP. Configura la conexion primero:

session_configure("127.0.0.1", 6380, 3600)  // host, puerto, TTL en segundos

Usa sesiones en tus handlers:

fn login(req: Request) -> Response {
    var resp: Response = response_text(200, "Logged in")
    let sid: String = session_start(req, resp)
    session_set(req, "user", "alice")
    return resp
}

fn profile(req: Request) -> Response {
    let user: String = session_get(req, "user")
    if user.length() == 0 {
        return response_text(401, "Not logged in")
    }
    return response_text(200, "Hello, " + user)
}

Archivos estaticos

Sirve archivos desde un directorio con serve_static:

serve_static(app, "public")

Las solicitudes a /style.css serviran public/style.css. Las rutas con comodin como /learn/* tambien pueden usarse para logica personalizada de servicio de archivos estaticos.

Ejemplo completo: una API de tareas

import "std/web"

var todos: Array = []

fn list_todos(req: Request) -> Response {
    let result: Map = Map.new()
    result.insert("count", int_to_string(todos.length()))
    return response_json_map(200, result)
}

fn add_todo(req: Request) -> Response {
    let data: Map = req_json(req)
    let title: String = data.get("title")
    todos.push(title)
    return response_json(201, "{\"ok\":true}")
}

fn main() {
    cors_configure("*", "GET,POST", "Content-Type")

    let app: App = app_new()
    app_use(app, mw_logging)
    app_use(app, mw_cors)
    app_get(app, "/todos", list_todos)
    app_post(app, "/todos", add_todo)
    serve_app(app, 3000, 64)
}

Resumen

← Anterior: El gestor de paquetes Siguiente: Tiempo real con Pub/Sub →