Crates.io | restify |
lib.rs | restify |
version | 0.0.6 |
source | src |
created_at | 2024-07-10 20:19:01.386005 |
updated_at | 2024-08-16 13:20:50.720239 |
description | STILL WIP |
homepage | https://github.com/coders-collab-org/restify |
repository | https://github.com/coders-collab-org/restify |
max_upload_size | |
id | 1298721 |
size | 10,987 |
Restify is a powerful Rust framework designed to streamline the process of building web servers, offering support for multiple web frameworks like Axum, Actix, Rocket (and more in the future), while seamlessly integrating OpenAPI specification generation for effortless API documentation.
Add the following to your Cargo.toml
file, choosing the feature for your desired web framework:
[dependencies]
restify = { version = "0.0.3", features = ["axum"] }
# Dependencies for your chosen web framework (example for Axum)
axum = "0.6"
tokio = { version = "1", features = ["full"] }
1. Define your data structures:
// /todo/entities.rs
use serde::Serialize;
#[derive(Debug, Serialize, Clone)]
pub struct TodoEntity {
pub id: String,
pub name: String,
pub done: bool,
}
// /todo/dto.rs
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct CreateTodoDto {
pub name: String,
}
#[derive(Debug, Deserialize)]
pub struct UpdateTodoDto {
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub done: Option<bool>,
}
2. Create your service:
// src/todo/service.rs
use std::collections::HashMap;
use restify::prelude::*;
use uuid::Uuid;
use crate::app::AppState;
use super::{
dto::{CreateTodoDto, UpdateTodoDto},
entities::TodoEntity,
};
use axum::{extract::State, http::StatusCode, response::Response};
#[derive(Injectable)]
pub struct TodoService {
#[from_request(via(State))]
state: AppState,
}
impl TodoService {
pub async fn get_all(&self) -> HashMap<String, TodoEntity> {
self.state.store.lock().await.clone()
}
pub async fn get_one(&self, id: String) -> Option<TodoEntity> {
self.state.store.lock().await.get(&id).cloned()
}
pub async fn create(&self, dto: CreateTodoDto) -> TodoEntity {
let id = Uuid::new_v4().to_string();
let todo = TodoEntity {
id: id.clone(),
name: dto.name,
done: false,
};
self
.state
.store
.lock()
.await
.insert(id.clone(), todo.clone());
todo
}
pub async fn update(&self, id: String, dto: UpdateTodoDto) -> Result<TodoEntity, Response> {
let mut store = self.state.store.lock().await;
if let Some(todo) = store.get_mut(&id) {
if let Some(name) = dto.name {
todo.name = name;
}
if let Some(done) = dto.done {
todo.done = done;
}
return Ok(todo.clone());
}
return Err(
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(().into())
.unwrap(),
);
}
pub async fn delete(&self, id: String) -> Result<TodoEntity, Response> {
self.state.store.lock().await.remove(&id).ok_or_else(|| {
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(().into())
.unwrap()
})
}
}
3. Create your controller:
// src/todo/controller.rs
use axum::{extract::Path, response::Response, Json};
use restify::prelude::*;
use tower_http::trace::TraceLayer;
use super::{
dto::{CreateTodoDto, UpdateTodoDto},
entities::TodoEntity,
services::TodoService,
};
#[derive(Injectable)]
pub struct TodoController {
service: TodoService,
}
#[controller("/todo", wrap = TraceLayer::new_for_http())]
impl TodoController {
#[get]
async fn get_all(self) -> Json<HashMap<String, TodoEntity>> {
Json(self.service.get_all().await)
}
// ... other routes for creating, updating, and deleting todos
}
4. Define your modules:
/// src/todo/mod.rs
use restify::Module;
#[derive(Module)]
#[module(controllers(TodoController))]
pub struct TodoModule;
// src/app.rs
use std::{collections::HashMap, ops::Deref, sync::Arc};
use restify::prelude::*;
use tokio::sync::Mutex;
use tower_http::trace::TraceLayer;
use crate::todo::{entities::TodoEntity, TodoModule};
#[derive(Module)]
#[module(imports(TodoModule), controllers(AppController))]
pub struct AppModule;
pub struct AppController;
#[controller("/", wrap = TraceLayer::new_for_http())]
impl AppController {
#[get]
async fn up() -> &'static str {
"UP!"
}
}
#[derive(Clone, Default)]
pub struct AppState(Arc<AppStateInner>);
#[derive(Default)]
pub struct AppStateInner {
pub store: Mutex<HashMap<String, TodoEntity>>,
}
impl Deref for AppState {
type Target = AppStateInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
6. If you use Axum and have state, you can create a restify.toml file to define the state path, so you don't have to add it in every Module
, Controller
, or Injectable
like #[module(state(AppState))]
# restify.toml
state = "crate::app::AppState"
5. Create your application (example for Axum):
use app::{AppModule, AppState};
use restify::axum::IntoRouter; // Import specific to Axum
mod app;
mod todo;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
axum::serve(
listener,
AppModule
.into_router(&mut ())
.with_state(AppState::default()),
)
.await
.unwrap();
}
Restify offers configuration options through the controller
and Module
macros. These options allow you to specify:
Contributions are welcome! If you'd like to contribute to Restify, please follow these steps:
This project is licensed under the [MIT License][license].