#resolver #api #request #macro-derive #user-name

macro resolver_api_derive

derive macros for HasResponse and request resolver

12 releases (3 major breaking)

3.0.0 Oct 29, 2024
2.0.0 Sep 16, 2024
1.1.0 May 18, 2024
0.1.8 Apr 10, 2024
0.1.5 Jun 18, 2023

#47 in #resolver

Download history 91/week @ 2025-04-08 52/week @ 2025-04-15 58/week @ 2025-04-22 87/week @ 2025-04-29 93/week @ 2025-05-06 88/week @ 2025-05-13 66/week @ 2025-05-20 71/week @ 2025-05-27 109/week @ 2025-06-03 103/week @ 2025-06-10 45/week @ 2025-06-17 199/week @ 2025-06-24 115/week @ 2025-07-01 94/week @ 2025-07-08 113/week @ 2025-07-15 86/week @ 2025-07-22

436 downloads per month
Used in 5 crates (via resolver_api)

GPL-3.0-or-later

5KB
87 lines

Resolver API

Declare your API in the Rust type system.

Declare API

/// User entity
#[derive(Serialize, Deserialize)]
pub struct User {
  pub id: i64,
  pub username: String,
}

/// Get User request 
#[derive(Serialize, Deserialize, Debug, Resolve)]
#[response(User)]
pub struct GetUser {
  pub id: i64,
}

Implement API

The API can be implemented using any transport. A common one is HTTP, for example using axum:

impl Resolve<()> for GetUser {
  async fn resolve(self, _: &()) -> Result<User, std::convert::Infallible> {
    Ok(User { id: self.id, username: String::new("example") }))
  }
}

struct Response(axum::response::Response);

impl<T> From<T> for Response
where
  T: serde::Serialize,
{
  fn from(value: T) -> Self {
    Response(axum::Json(value).into_response())
  }
}

#[derive(Deserialize, Resolve)]
#[response(Response)]
#[error(Response)]
enum Request {
  GetUser(GetUser),
}

let app = Router::new()
  .route(
    "/",
    post(
      |Json(req): Json<Request>| async move {
        match req.resolve(&()).await {
          Ok(res) => res.0,
          Err(err) => err.0,
        }
      },
    ),
  );

let listener = tokio::net::TcpListener::bind("127.0.0.1:5555")
  .await?;

axum::serve(listener, app).await?;

Call API

fn reqwest() -> &'static reqwest::Client {
  static REQWEST: OnceLock<reqwest::Client> = OnceLock::new();
  REQWEST.get_or_init(reqwest::Client::default)
}

async fn resolve<T>(req: &T) -> Result<T::Response, Error> {
  let res = client
    .post("http://127.0.0.1:5555")
    .json(req)
    .send()
    .await?;
  if res.status().is_success() {
    res.json().await
  } else {
    // handle error
  }
}

// knows response is "User" type
let user = resolve(&GetUser { id: 0 })
  .await
  .unwrap(); 

Dependencies

~190–610KB
~15K SLoC