
Rust API - Creating the Get by ID Route with Rust - Part V
The GET /api/tasks/{id} handler extracts the UUID with Path<Uuid> and fetches the task with SQLx query_as! into TaskModel. It returns the task as JSON or a JSON error.
First we’ll deal with our services file, where we’ll import our Path, to recognize from it the id or uuid in this case, which we’ll work on our search query:
use actix_web::{
get,
post,
web::{
Data,
Json,
scope,
Query,
Path,
ServiceConfig
},
HttpResponse,
Responder
};
And we’ll write our function below:
#[get("/tasks/{id}")]
async fn get_task_by_id(path: Path<uuid::Uuid>, data: Data<AppState>) -> impl Responder {
}
Inside the scope of our function, we’ll start to verify from our URL the task_id, or the uuid from our URL:
#[get("/tasks/{id}")]
async fn get_task_by_id(path: Path<uuid::Uuid>, data: Data<AppState>) -> impl Responder {
let task_id = path.into_inner();
}
Next point is to make our query and error handling:
#[get("/tasks/{id}")]
async fn get_task_by_id(path: Path<uuid::Uuid>, data: Data<AppState>) -> impl Responder {
let task_id = path.into_inner();
let query_result = sqlx
::query_as!(TaskModel, "SELECT * FROM tasks WHERE id = $1", task_id)
.fetch_one(&data.db).await;
match query_result {
Ok(task) => {
let task_note = json!({
"status": "success",
"task": task
});
return HttpResponse::Ok().json(task_note);
}
Err(error) => {
return HttpResponse::InternalServerError().json(
json!({
"status": "error",
"message": format!("{:?}", error)
})
)
}
}
}
Finally, add it to our config:
pub fn config(conf: &mut ServiceConfig) {
let scope = scope("/api")
.service(health_checker)
.service(create_task)
.service(get_all_tasks)
.service(get_task_by_id);
conf.service(scope);
}
Our file in the end looks like this:
src/services.rs
use crate::{
model::TaskModel,
schema:: {
CreateTaskSchema,
FilterOptions
}, AppState
};
use actix_web::{
get,
post,
web::{
Data,
Json,
scope,
Query,
Path,
ServiceConfig
},
HttpResponse,
Responder
};
use serde_json::json;
#[get("/healthchecker")]
async fn health_checker() -> impl Responder {
const MESSAGE: &str = "Health check API is up and running smoothly.";
HttpResponse::Ok().json(json!({"status": "success", "message": MESSAGE }))
}
#[post("/task")]
async fn create_task(body: Json<CreateTaskSchema>, data: Data<AppState>) -> impl Responder {
match
sqlx
::query_as!(
TaskModel,
"INSERT INTO tasks (title, content) VALUES ($1, $2)
RETURNING * ",
body.title.to_string(),
body.content.to_string()
)
.fetch_one(&data.db)
.await {
Ok(task) => {
let task_note = json!({
"status": "success",
"task": task
});
return HttpResponse::Ok().json(task_note);
}
Err(error) => {
return HttpResponse::InternalServerError().json(
json!({
"status": "error",
"message": format!("{:?}", error)
})
)
}
}
}
#[get("/tasks")]
pub async fn get_all_tasks(opts: Query<FilterOptions>, data: Data<AppState>) -> impl Responder {
let limit = opts.limit.unwrap_or(10);
let offset = (opts.page.unwrap_or(1) - 1) * limit;
match
sqlx
::query_as!(
TaskModel,
"SELECT * FROM tasks ORDER by id LIMIT $1 OFFSET $2",
limit as i32,
offset as i32
)
.fetch_all(&data.db)
.await {
Ok(task) => {
let task_note = json!({
"status": "success",
"task": task
});
return HttpResponse::Ok().json(task_note);
}
Err(error) => {
return HttpResponse::InternalServerError().json(
json!({
"status": "error",
"message": format!("{:?}", error)
})
)
}
}
}
#[get("/tasks/{id}")]
async fn get_task_by_id(path: Path<uuid::Uuid>, data: Data<AppState>) -> impl Responder {
let task_id = path.into_inner();
let query_result = sqlx
::query_as!(TaskModel, "SELECT * FROM tasks WHERE id = $1", task_id)
.fetch_one(&data.db).await;
match query_result {
Ok(task) => {
let task_note = json!({
"status": "success",
"task": task
});
return HttpResponse::Ok().json(task_note);
}
Err(error) => {
return HttpResponse::InternalServerError().json(
json!({
"status": "error",
"message": format!("{:?}", error)
})
)
}
}
}
pub fn config(conf: &mut ServiceConfig) {
let scope = scope("/api")
.service(health_checker)
.service(create_task)
.service(get_all_tasks)
.service(get_task_by_id);
conf.service(scope);
}
To test, we’ll create a new task:
curl --request POST \
--url http://localhost:8080/api/task \
--header 'Content-Type: application/json' \
--data '{
"title": "title test2",
"content": "content test"
}'
It should generate this task where I’ll grab our id:
{
"status": "success",
"task": {
"content": "content test",
"created_at": "2023-06-10T15:05:53.710564Z",
"id": "d6b52611-8117-43d3-a8f6-56732ac94bd5",
"title": "title test2"
}
}
And we can test it as follows:
curl --request GET \
--url http://localhost:8080/api/tasks/d6b52611-8117-43d3-a8f6-56732ac94bd5
And we get the following result:
{
"task":{
"content":"content test",
"created_at":"2023-06-10T15:05:53.710564Z",
"id":"d6b52611-8117-43d3-a8f6-56732ac94bd5",
"title":"title test2"
}
"status":"success"
}
And well, that’s it for today!