Rust-Base-Backend is a foundational backend project written in Rust, designed with a clean architecture using the services-repositories pattern. This project serves as a starting point for developing robust and efficient backend systems using the Rust programming language.
- Modular and scalable architecture
- RESTful API setup
- Docker support for containerization
- Middleware for parameter validation
- Error handling conforming to RFC 7807
- Asynchronous programming with Tokio
- Swagger integration for API documentation using OpenAPI
The project is structured to follow the principles of clean architecture, ensuring separation of concerns and maintainability. The main components include:
Services contain the business logic of the application. They interact with repositories to perform operations and handle the core functionality.
Repositories are responsible for data access and storage. They provide an abstraction layer over the data sources, making it easier to switch between different storage solutions. For demonstration purposes, the project includes a basic example where the repository simulates a database using an in-memory array.
Controllers handle incoming HTTP requests, interact with services, and return appropriate responses. They act as a bridge between the API endpoints and the business logic.
Here is an example demonstrating how to create a controller and define a simple endpoint:
use warp::Filter;
use serde_json::json;
// Controller function
pub async fn get_health() -> Result<impl warp::Reply, warp::Rejection> {
Ok(warp::reply::json(&json!({
"status": "ok"
})))
}
// Route definition
pub fn health_route() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("health")
.and(warp::get())
.and_then(get_health)
}
// Adding the endpoint to the router in main function
#[tokio::main]
async fn main() {
let routes = health_route();
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
Models define the structure of the data used throughout the application and are divided into two main categories:
-
Database Models: These models represent the data as it is stored in the database. They define the schema and are used by repositories to interact with the database layer.
-
Data Transfer Objects (DTOs): DTOs are used for input and output in the API. They define the structure of data that is sent to and received from the API endpoints, ensuring that only the necessary information is exposed.
The project includes a middleware for validating input parameters to ensure they meet the required criteria before being processed by the application. This is implemented using:
- base_validator.rs: Defines the base validation logic.
- validator.rs: Contains specific validation rules and types.
The middleware for input parameter validation in this project ensures that incoming requests meet the required criteria before being processed by the services. It leverages various validation rules to check the integrity and format of the input data.
Example of using the validator middleware:
pub fn validate_create_message(path:Option<String>) -> impl Filter<Extract = (CreateMessageModelDto,), Error = Rejection> + Clone {
let path = warp::any().map(move || path.clone());
warp::body::json()
.and(path)
.and_then(|body: CreateMessageModelDto, path: Option<String>| async move {
let content_validation = match Rule::new(body.content.as_ref(),Some("content".to_string()), path)
.not_null()
.with_error_code(ErrorCodes::NotNull)
.not_empty()
.with_error_code(ErrorCodes::NotEmpty)
.max_length(32)
.with_error_code(ErrorCodes::MaxSize)
.validate() {
Ok(_) => Ok(body),
Err(err) => Err(err)
};
content_validation
.map(|body| body)
})
}
The ValidationRule
enum in the validator.rs
file defines various types of validation rules:
Validation | Description |
---|---|
NotNull | Ensures that the input value is not null. |
NotEmpty | Ensures that the input string is not empty. |
MaxLength | Checks that the input string does not exceed a specified length. |
WithinRange | Ensures that the input value is within a specified range. |
IsInteger | Validates that the input is an integer. |
IsDecimal | Validates that the input is a decimal number. |
IsNumber | Ensures that the input is a valid number (integer or decimal). |
HasDecimals | Checks if the input number has a decimal part. |
The project includes an error handler that returns errors in accordance with RFC 7807, the standard for problem details in HTTP APIs. This ensures that error responses are consistent and informative, providing clear details about the issues encountered.
Here is an example of an error response conforming to RFC 7807:
{
"type": "https://example.com/probs/invalid-input",
"title": "Invalid input",
"status": 400,
"instance": "/request/12345",
"details": [
{
"field": "email",
"message": "Email format is invalid",
"error_code": 4010
}
]
}
The repository in the project includes a basic example where it simulates a database using an in-memory array. This example demonstrates how to store, retrieve, and manipulate data without the need for an actual database. This approach is useful for testing and development purposes.
The project utilizes Tokio, an asynchronous runtime for the Rust programming language, to handle asynchronous operations efficiently. Tokio allows the application to handle many tasks concurrently without blocking the execution thread, which is especially useful for I/O-bound operations like handling multiple HTTP requests.
Here is a simple example demonstrating the use of async/await in the project:
use tokio::time::{sleep, Duration};
async fn process_request() {
// Simulate a delay
sleep(Duration::from_secs(2)).await;
println!("Request processed");
}
#[tokio::main]
async fn main() {
// Call the asynchronous function
process_request().await;
// Additional async operations
let routes = health_route();
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
The project integrates Swagger for API documentation using OpenAPI. This allows for automatically generated and interactive API documentation, making it easier for developers to understand and interact with the API endpoints.
To enable Swagger documentation in the project, ensure that the necessary dependencies are included and configured to generate the OpenAPI specification, which can then be served and viewed using tools like Swagger UI.
- Clone the repository:
git clone https://github.com/LuigimonSoft/Rust-Base-Backend.git
cd Rust-Base-Backend
- Build the project:
cargo build
- Run the project
cargo run
Contributions are welcome! Please fork this repository and submit pull requests.
This project is licensed under the MIT License. See the LICENSE file for details.
For any questions or issues, please open an issue on this repository.