Contraband is a web framework built on top of actix for creating modular applications in Rust with dependency injection and performant higher-level abstractions.
Contraband is heavily inspired by Spring Boot and Nestjs.
For more information you can check out the examples.
Create an empty project with Cargo
$ cargo new example-project
$ cd example-project
Add contraband and actix_web to the list of dependencies in Cargo.toml
[dependencies]
actix-web = "3"
contraband = "^0.1.0"
Controllers are responsible for handling incoming request and is able to serve multiple routes and HTTP-method types.
In order to create a controller we use structs and attributes. Attributes are used to generate the boilerplate necessary to register all routes and inject all dependencies into the controller.
use contraband::{Injectable, controller};
use contraband::core::ContrabandApp;
use actix_web::HttpResponse;
#[derive(Clone, Injectable)]
struct HelloController;
#[controller]
impl HelloController {
#[get]
async fn hello_world(self) -> HttpResponse {
HttpResponse::Ok().body("Hello world!")
}
}
Note the derive clause. In order to be injected into the Contraband app it needs to derive Injectable. Since Contraband spawns multiple instances it also needs to derive Clone.
One of the biggest perks of Contraband is the ability to inject arbitrary dependencies into, for example, our controllers. This is done through deriving the aforementioned Injectable-trait.
Below is and example of how an injectable struct can be used for user management.
#[derive(Clone, Injectable)]
pub struct UserService;
impl UserService {
pub async fn add_user(&self, name: &str) {
// stub
}
pub async fn get_user(&self, id: u32) {
// stub
}
}
In order to utilize our new service we can inject it into our HelloController
by defining an Arc
-pointer to our service.
// ...
#[derive(Clone, Injectable)]
struct HelloController {
user_service: std::sync::Arc<UserService>
}
// ...
We are now able to use our new service in any methods in HelloController
.
Below we use our new service for fetching users in a new route.
#[controller]
impl HelloController {
// ...
#[get("/users/:id")]
async fn get_users(self, id: actix_web::web::Path<i32>) -> HttpResponse {
let name = self.user_service.get_user(id);
HttpResponse::Ok().body(name)
}
// ...
}
However in order for the dependency to be resolved when initializing the Contraband runtime we need to register it as a provider. More on that in the next chapter.
A module is a struct that derives the module-trait and is used to organize our controllers and dependencies into nested building blocks.
use contraband::core::ContrabandApp;
use contraband::module;
#[module]
#[controller(HelloController)]
#[provider(UserService)]
struct AppModule;
#[contraband::main]
async fn main() -> std::io::Result<()> {
ContrabandApp::new()
.start::<AppModule>()
.await
}
Note how we registered our UserService as a provider. This is required in order to inject the dependency in this scope and enabling its use in our controller.
Contraband is licensed under either MIT licensed or Apache 2.0 licensed.