This is part 3 of our Rust REST API Actix-web PostgreSQL post with sample codes that does CRUD operations on a database from Actix-web. We referred to these codes in the previous post.
Rust Main Function – main.rs
The main.rs file has the following main() function codes that define endpoints in Actix-web.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | mod person; use actix_web::{web, App, HttpServer, FromRequest, Responder}; use log::{info, trace, warn}; // Handler functions here fn main() { HttpServer::new(|| { App::new() .service(web::resource("/persons/{person_id}") .route(web::get().to(get_person)) .route(web::delete().to(delete_person)) .route(web::put().to(update_person))) .service(web::resource("/persons") .route(web::get().to(get_person_list)) .route(web::post().to(create_person))) }) .bind("127.0.0.1:8088") .unwrap() .run() .unwrap(); } |
Each service has a unique resource that has routes. For instance, the resource /persons/{person_id} allows for HTTP GET , HTTP DELETE , and HTTP PUT methods. Also, each of these methods triggers a handler function.
Handler Functions For Rust REST API (Web Layer)
These are “web controller”-level functions with codes that delegate calls to our “persistent layer” (person module).
Function – get_person_list
First, we have the get_person_list function, which retrieves all the person records from the database through the person module.
1 2 3 4 5 | fn get_person_list() -> impl Responder { let mut vec:Vec<person::common::Person> = Vec::new(); person::get_person_all(&mut vec); return web::Json(vec); } |
Function – get_person
Second, we have the get_person function. This function retrieves a particular person record from the database by person_id.
1 2 3 4 5 6 7 | // Use i32 for int and serial in postgresql fn get_person(info: web::Path<(i32)>) -> impl Responder { let mut vec:Vec<person::common::Person> = Vec::new(); person::get_person_by_id(&mut vec, info.into_inner()); return web::Json(vec); } |
Function – create_person
Next, we have the create_person function. This function creates a person record in the database.
1 2 3 4 | fn create_person(info: web::Json<person::common::CreatePersonRequest>) -> impl Responder { person::create_person(info.into_inner().person_name); return web::Json({}); } |
Function – update_person
Next, we have a function to update a person’s name in the database.
1 2 3 4 | fn update_person(id: web::Path<(i32)>, payload: web::Json<person::common::UpdatePersonRequest>) -> impl Responder { person::update_person(id.into_inner(), payload.into_inner().person_name); return web::Json({}); } |
Function – delete_person
Lastly, we have a function to delete a person record in the database by person_id.
1 2 3 4 | fn delete_person(info: web::Path<(i32)>) -> impl Responder { person::delete_person(info.into_inner()); return web::Json({}); } |
Person Module – Struct and DB codes for Actix-Web and PostgreSQL
Common Structs both for Actix-Web and PostgreSQL codes – common.rs
This file contains the structs for our Rust codes. Note that we use the same structs in the controller layer in main.rs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | use serde::Serialize; use serde::Deserialize; // This represents the payload of an HTTP Response #[derive(Serialize)] pub struct Person { pub person_id: i32, pub person_name: String } // This represent the payload of an HTTP Request #[derive(Deserialize)] pub struct CreatePersonRequest { pub person_name: String } // This represent the payload of an HTTP Request #[derive(Deserialize, Debug)] pub struct UpdatePersonRequest { pub person_name: String } |
The struct Person also represents a person record in the database. Meanwhile, the others automatically map the JSONs from incoming requests to their instances.
Persistence Module for Actix-web / PostgreSQL – mod.rs
The first three lines make the common mod available and use the extern and use statements for postgres crate.
1 2 3 4 | pub mod common; extern crate postgres; use postgres::{Connection, TlsMode}; ... |
Here, we store the connection string for PostgreSQL in a global static variable.
1 2 3 4 | ... // Check your docker-compose.yml for PostgreSQL for username and password static pg_connection_string: &str = "postgresql://turreta:a1128f69-e6f7-4e93-a2df-3d4db6030abc@localhost:5442"; ... |
The rest of the codes are functions that directly access the database.
Function – get_person_all
First, we have the get_person_all function. This function retrieves all person records from the database and puts them in a Vec<common::Person> instance.
1 2 3 4 5 6 7 8 9 10 11 | pub fn get_person_all(vec: &mut Vec<common::Person>) { let conn = Connection::connect(pg_connection_string, TlsMode::None) .unwrap(); for row in &conn.query("SELECT person_id, person_name FROM persons", &[]).unwrap() { let person = common::Person { person_id: row.get(0), person_name: row.get(1) }; vec.push(person); } } |
Function – get_person_by_id
Second, we have a function that retrieves a person’s record by person_id. The function pus the record in a Vec<common::Person> instance.
1 2 3 4 5 6 7 8 9 10 | pub fn get_person_by_id(vec: &mut Vec<common::Person>, id: i32) { let conn = Connection::connect(pg_connection_string, TlsMode::None) .unwrap(); for row in &conn.query("SELECT person_id, person_name FROM persons WHERE person_id = $1", &[&id]).unwrap() { let person = common::Person { person_id: row.get(0), person_name: row.get(1) }; vec.push(person); } } |
Function – create_person
This function creates a person record in the database.
1 2 3 4 | pub fn create_person(person_name: String) { let conn = Connection::connect(pg_connection_string, TlsMode::None) .unwrap(); conn.execute("INSERT INTO PERSONS (PERSON_NAME) VALUES ($1)", &[&person_name]); } |
Function – delete_person
This function deletes a person record in the database by person_id.
1 2 3 4 | pub fn delete_person(id: i32) { let conn = Connection::connect(pg_connection_string, TlsMode::None) .unwrap(); conn.execute("DELETE FROM PERSONS WHERE PERSON_ID = $1", &[&id]); } |
Function – update_person
This function updates a person’s name in the database by person_id.
1 2 3 4 | pub fn update_person(id: i32, person_name: String) { let conn = Connection::connect(pg_connection_string, TlsMode::None) .unwrap(); conn.execute("UPDATE PERSONS SET PERSON_NAME = $1 WHERE PERSON_ID = $2", &[&person_name, &id]); } |
That’s all for this Rust REST API Actix-web PostgreSQL and its codes.