QuickAPI is rust package for building restful APIs for sea ORM models and axum framework.
Warning
This project is in early development stage. API may change without notice.
This package provides easy way to create RESTful API endpoints for your sea ORM models using axum framework. It not only allows simple CRUD operations, it also provides a way how to add when conditions to whole views.
Each "view" provides single type of operation on single entity type. Operations are:
- List - list all entities of given type
- Create - create new entity of given type
- Detail - detail view of single entity
- Update - update single entity
- Delete - delete single entity
Each one of these operations (except of create) have ability to filter select query.
You can provide multiple filters that resemble to axum handlers, where first argument is Select on given Model Entity,
And other arguments are axum::extract::FromRequest
types that are used to filter the query.
You can also provide "when" conditions that clone given view and add you ability to change it.
When accepts function that resembles to axum handlers, when arguments are axum::extract::FromRequest
and you need to return Result<()>
when this condition is met or
Result<(), quickapi_when::Error> when it is not met.
If you return NoMatch
quickapi will continue to evaluate next when condition.
List view is used to list all entities of given type. You can filter them, and use predefined filter basic blocks such as:
- Paginator - This filter allows you to paginate results by providing
page
andper_page
query parameters.
#[derive(Debug, Deserialize)]
pub struct QueryFormat {
format: Option<String>,
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct UsernameOnly(pub String);
impl From<entity::UserModel> for UsernameOnly {
fn from(user: entity::UserModel) -> Self {
UsernameOnly(user.username)
}
}
// filter_search_query filters the search query
pub async fn filter_search_query_username(
query: Select<entity::User>,
search: Query<QuerySearch>,
) -> Result<Select<entity::User>, quickapi_filter::Error> {
// if query is present, filter by username
Ok(if let Some(s) = search.0.query {
query.filter(entity::user::Column::Username.contains(s))
} else {
query
})
}
// add list view for User entity
let router = api
.list::<entity::User>("/api/user")?
.with_filter(Paginator::default())
.with_filter(filter_search_query_username)
.with_serializer::<UsernameOnly>()
.wrap_result_key("users")
.when(async move |search: Query<QuerySearch>| {
if search.query.is_some() {
Ok(())
} else {
Err(quickapi_when::Error::NoMatch)
}
}, |v| {
// change serializer for this condition
Ok(v.with_serializer::<serializers::SimpleUser>())
})?.register_router(router)?;
Detail view is used to get single entity by single field, usually by primary key. You can filter it, use when conditions and serializers.:
#[derive(Debug, Deserialize)]
pub struct QueryFormat {
format: Option<String>,
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct UsernameOnly(pub String);
impl From<entity::UserModel> for UsernameOnly {
fn from(user: entity::UserModel) -> Self {
UsernameOnly(user.username)
}
}
/// when_condition is a condition that will be checked before applying the view
pub async fn when_condition_format(_x: Query<QueryFormat>) -> Result<(), quickapi_when::Error> {
match &_x.format {
Some(format) if format == "full" => Ok(()),
_ => Err(quickapi_when::Error::NoMatch),
}
}
// add detail view for User entity
let router = api
.detail::<entity::User>("/api/user/{id}", PrimaryKey::Path("id".into()))?
.with_serializer::<UsernameOnly>()
.wrap_result_key("user")
.when(when_condition_format, |v| {
Ok(v.with_serializer::<serializers::SimpleUser>())
})?.register_router(router)?;
Create view is used to create new entity of given type. It accepts JSON body with entity data and returns created entity.
Warning
Not implemented yet. Work in progress.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct CreateUser {
pub username: String,
}
impl From<CreateUser> for entity::UserModel {
fn from(create_user: CreateUser) -> Self {
entity::UserModel {
id: 0, // Assuming the ID is auto-generated by the database
username: create_user.username,
}
}
}
// Create View example (Still in development)
let router = api
.create::<entity::User>("/api/user")?
.with_serializer::<CreateUser>()
.with_before_save(async move |m: entity::UserModel| {
// do something with model before saving
debug!("Before save: {:?}", m);
Ok(m)
})
.register_router(router)?;
Update view is used to update single entity by single field, usually by primary key.
Warning
Not implemented yet. Work in progress.
Delete view is used to delete single entity by single field, usually by primary key.
Warning
Not implemented yet. Work in progress.
Working example is available in example directory.
This project is in early development stage. A lot of work was already done, but there is still a lot to do. A lot of features are working, but there need to be some polishing and testing. List and Detail views are implemented, but Create, Update and Delete views are not yet implemented.
- Paginator not filter, but separate field in views that it supports
Peter Vrba [email protected]