| Crates.io | laraxum |
| lib.rs | laraxum |
| version | 0.1.4 |
| created_at | 2025-07-24 10:15:27.862682+00 |
| updated_at | 2025-08-22 07:22:51.709214+00 |
| description | Create API database servers easily using Axum and SQLX. |
| homepage | |
| repository | https://github.com/coolCucumber-cat/laraxum.git |
| max_upload_size | |
| id | 1765880 |
| size | 195,753 |
Create API database servers easily using Axum and SQLX. It is inspired by Laravel and uses the MVC paradigm:
This framework is only responsible for creating backend half of an app, meaning the model and controller. You can create your own frontend, meaing the view. Or you can just use it as an API and interact with the controller.
A model manages the data storage and interacts with the database. It implements the collection trait:
get_allcreate_oneand the model trait:
get_oneupdate_onedelete_oneA manymodel is similar to a model but with two columns. The manymodel trait can be implemented for each column. The column will be used as an id for multiple values in the other column.
This can be used to create many-to-many relationships.
A controller manages the connection between model and view. It implements the controller trait:
get_manygetcreateupdatedeleteThe State type is the type that will be available as context in the controller,
which contains the database connection.
The Auth type is the type that will authenticate the request.
AuthToken<()> doesn't do any authentication.
You can implement the authenticate trait for custom authentication and use it like AuthToken<T>.
Implement the authenticate trait to allow custom authentication that can be used in the controller.
AuthToken<T> should be returned from the login endpoint, which will encrypt it.
To authenticate the request, it will be decrypted.
Implement the authorize trait for custom authorization. You must specify a type that is authenticatable
struct UserAuth {
admin: bool,
}
impl From<bool> for UserAuth {
fn from(admin: bool) -> Self {
Self { admin }
}
}
impl From<UserAuth> for bool {
fn from(user: UserAuth) -> Self {
user.admin
}
}
impl Authenticate for UserAuth {
type State = AppDb;
fn authenticate(&self, state: &Arc<Self::State>) -> Result<(), AuthError> {
// authenticate the user
Ok(())
}
}
struct UserAuthAdmin;
impl Authorize for UserAuthAdmin {
type Authenticate = UserAuth;
fn authorize(authorize: Self::Authenticate) -> Result<Self, AuthError> {
if authorize.admin {
Ok(UserAuthAdmin)
} else {
Err(AuthError::Unauthorized)
}
}
}
mod AppDb {
#[db(model(), controller())]
struct Anyone {}
#[db(model(), controller(auth(User)))]
struct UserOnly {}
#[db(model(), controller(auth(UserAdmin)))]
struct AdminOnly {}
}
The database is defined using the db attribute macro on a module:
name: Option<String>, the name of the databaseEach struct in the module is a table. Use the db attribute on the struct:
name: Option<String>, the name of the tablemodel: Option<struct>, implement model and use these options
many: Option<bool>, implement a manymodel instead of modelcontroller: Option<struct>, implement controller and use these options
auth: Option<Type>, the type to use for authentication and authorizationEach field in the struct is a column. Use the db attribute on the field:
name: Option<String>, the name of the columnty: Option<enum>
id primary keyforeign: struct, foreign key
many: Option<Ident>, a many-to-many relationship with the named tableon_create the time this entity was createdon_update the time this entity was last updatevarchar: u16 string type with dynamic length, set max lengthchar: u16, string type with fixed length, set lengthtext string type for extra large stringsresponse: Option<struct>
name: Option<String>, the name of the field in the response when serializedskip: bool, skip the field in the response when serializedrequest:
name: Option<String>, the name of the field in the request when deserializedvalidate: Option<[enum]>
min_len: Exprfunc: Exprmatches: Patn_matches: Pateq: Exprn_eq: Exprgt: Exprlt: Exprgte: Exprlte: Exprreal_ty: Option<Type>, when using wrapper types, this is the inner typeunique: Option<bool>, this column is uniqueindex: Option<struct>, create an index that can filter using this column, like <User as CollectionIndexOne<UserEmail>>
name: Ident, the name of the indexrequest_ty: Option<Type>, the type used to indexrequest_ty_ref: bool, the type used to index is a referenceIf you don't use the controller option, the controller won't be implemented. You can implement it yourself or not at all. The functions have a default implementation so you don't have to implement each one by yourself.
#[db(name = "database")]
pub mod AppDb {
#[db(name = "addresses", model(), controller())]
pub struct Address {
#[db(ty(id))]
id: u64,
#[db(ty(varchar = 255))]
street: String,
#[db(ty(char = 5))]
postcode: String,
#[db(ty(varchar = 255))]
city: String,
}
#[db(name = "contacts", model(), controller())]
pub struct Contact {
#[db(ty(id))]
id: u64,
#[db(ty(foreign()), request(name = "address_id"), name = "address_id")]
address: Address,
#[db(ty(varchar = 255))]
firstname: String,
#[db(ty(varchar = 255))]
lastname: String,
#[db(ty(varchar = 16))]
mobile: String,
#[db(ty(varchar = 16))]
landline: String,
#[db(ty(varchar = 255))]
email: String,
}
#[db(name = "groups", model(), controller())]
pub struct Group {
#[db(ty(id))]
id: u64,
#[db(ty(varchar = 255))]
title: String,
}
#[db(name = "users", model(), controller())]
pub struct User {
#[db(ty(id))]
id: u64,
#[db(ty(foreign(many(GroupUser))))]
groups: Vec<Group>, // many to many relationship
#[db(ty(foreign()), request(name = "contact_id"), name = "contact_id")]
contact: Contact,
#[db(ty(varchar = 255))]
name: String,
#[db(ty(varchar = 255), unique, index(name(UserEmail), request_ty(str), request_ty_ref = true))] // <User as CollectionIndexOne<UserEmail>>
email: String,
#[db(ty(varchar = 255), response(skip), request(validate(min_len(12))))]
password: String,
#[db(ty(on_create))]
created_at: chrono::DateTime<chrono::Utc>,
#[db(ty(on_update))]
updated_at: chrono::DateTime<chrono::Utc>,
admin: bool
}
#[db(name = "group_user", many_model)]
pub struct GroupUser {
#[db(ty(foreign()), request(name = "group_id"), name = "group_id")]
group: Group,
#[db(ty(foreign()), request(name = "user_id"), name = "user_id")]
user: User,
}
}