ohkami

ohkami - [狼] means wolf in Japanese - is simple and macro free web framework for Rust.


Features


0.4 → 0.5


Quick start

  1. Add dependencies:

toml [dependencies] ohkami = "0.5.0"

  1. Write your first code with ohkami:

```rust use ohkami::prelude::*;

fn main() -> Result<()> { Server::setup() .GET("/", || async { Response::OK("Hello, world!") }) .serve_on(":3000") } ```

  1. If you're interested in ohkami, learn more by examples and documentation !


Snippets

handle query params

rust let name = c.req.query::<&str>("name")?; // `::<&str>` isn't needed when it's presumable rust let count = c.req.query::<usize>("count")?; // `::<usize>` isn't needed when it's presumable

handle request body

```rust fn main() -> Result<()> { Server::setup() .GET("/api/users", reflect) .GET("/api/users/name", reflectname) .serveon(":3000") }

[derive(Serialize, Deserialize)]

struct User { id: i64, name: String, }

async fn reflect(user: JSON) -> Result { Response::OK(user) }

async fn reflect_name(user: JSON) -> Result { let name = user.name; // JSON implements Deref

Response::OK(name)

} ```

handle path params

```rust fn main() -> Result<()> { Server::setup() .GET("/sleepy/:time/:name", sleepyhello) .serveon("localhost:8080") }

async fn sleepyhello(time: u64, name: String) -> Result { (time < 30) .else(|| Response::BadRequest("sleeping time (sec) must be less than 30."))?; std::thread::sleep( std::time::Duration::from_secs(time) ); Response::OK(format!("Hello {name}, I'm extremely sleepy...")) } ```

return OK response with text/plain

rust Response::OK("Hello, world!") // without Context rust c.OK("Hello, world!") // with Context

return OK response with application/json

rust Response::OK(json!{"ok": true}) // or c.OK(json!{"ok": true}) rust Response::OK(json("Hello!")?) Response::OK(json(user)?) //or c.OK(json("Hello!")?) c.OK(json(user)?) // `json()` serializes Rust value into JSON // value has to implemant `serde::Serialize`

handle errors

```rust makeohkamiresult()?;

// or, you can add an error context message: makeohkamiresult() .else(|e| e.errorcontext("failed to get user data"))?;

// or discard original error: makeohkamiresult() .else(|| Response::InternalServerError("can't get user"))?; // or .else(|| Response::InternalServerError(None))?; rust makesomeerror() // can't use ? .else(|e| Response::InternalServerError(e.tostring()))?;

makesomeerror() .else(|| Response::InternalServerError(None))?; ```

handle Option values

rust let handler = self.handler.as_ref() ._else(|| Response::NotFound("handler not found"))?; // or ._else(|| Response::NotFound(None))?;

assert boolean conditions

rust (count < 10) ._else(|| Response::BadRequest("`count` must be less than 10"))?; // or ._else(|| Response::BadRequest(None))?;

log config

rust fn main() -> Result<()> { let config = Config { log_subscribe: Some( tracing_subscriber::fmt() .with_max_level(tracing::Level::TRACE) ), ..Default::default() }; Server::setup_with(config) .GET("/", || async {Response::OK("Hello!")}) }

DB config

rust let config = Config { db_profile: DBprofile { pool_options: PgPoolOptions::new().max_connections(20), url: DB_URL.as_str(), }, ..Default::default() };

use sqlx

rust let user = sqlx::query_as::<_, User>( "SELECT id, name FROM users WHERE id = $1" ).bind(1) .fetch_one(c.pool()) .await?; // `Response` implements `From<sqlx::Error>`

use middlewares

```rust fn main() -> Result<()> { let middleware = Middleware::new() .ANY("*", |c| async { tracing::info!("Hello, middleware!"); c });

Server::setup_with(middleware)
    .GET("/", || async {
        Response::OK("Hello!")
    })
    .serve_on("localhost:3000")

} rust fn main() -> Result<()> { let config = Config { logsubscribe: Some( tracingsubscriber::fmt() .withmaxlevel(tracing::Level::TRACE) ), ..Default::default() };

let middleware = Middleware::new()
    .ANY("/*", |c| async {
        tracing::info!("Hello, middleware!");
        c
    });

let thirdparty_middleware = some_external_crate::x;

Server::setup_with(config.and(middleware).and(x))
    .GET("/", || async {
        Response::OK("Hello!")
    })
    .serve_on("localhost:3000")

} ```

test

  1. split setup process from main function: ```rust fn server() -> Server { Server::setup() .GET("/", || async {Response::OK("Hello!")}) }

fn main() -> Result<()> { server().serve_on(":3000") } 2. import `test::Test` and other utils rust

[cfg(test)]

mod test { use ohkami::{server::Server, response::Response, test::{Test, Request, Method}}; use once_cell::sync::Lazy;

static SERVER: Lazy<Server> = Lazy::new(|| super::server());

#[test]
fn test_hello() {
    let request = Request::new(Method::GET, "/");
    SERVER.assert_to_res(&request, Response::OK("Hello!"));
    SERVER.assert_not_to_res(&request, Response::BadRequest(None));
}

} ```


Development

ohkami is on early stage now and not for producntion use. Please give me your feedback ! → GetHub issue


License

This project is licensed under MIT LICENSE (LICENSE-MIT or https://opensource.org/licenses/MIT).