`sqlx::FromRow` is a trait used to turn a database result row into a Rust struct. In a course API, deriving it for `Course` supports reading database records as values that handlers can return to clients, without writing field-by-field mapping code. section 6.2.1 section 6.2.2
#[derive(Serialize, Debug, Clone, sqlx::FromRow)]
pub struct Course {
pub course_id: i32,
pub tutor_id: i32,
pub course_name: String,
pub course_description: Option<String>,
pub course_format: Option<String>,
pub course_structure: Option<String>,
pub course_duration: Option<String>,
pub course_price: Option<i32>,
pub course_language: Option<String>,
pub course_level: Option<String>,
pub posted_time: Option<NaiveDateTime>,
}
The `Course` model derives `sqlx::FromRow` alongside `Serialize`, `Debug`, and `Clone`. Required identifiers and the course name use non-optional fields; fields that may lack database values use `Option<T>`. section 6.2.1
Keep the database record model distinct from request inputs. `Course` includes fields such as `course_id` and `posted_time` that the database can generate; `CreateCourse` carries the fields supplied when creating a course. `FromRow` applies when reading the resulting record into `Course`, not when deserializing the creation request. section 6.2.1
use crate::errors::EzyTutorError;
use crate::models::course::*;
use sqlx::postgres::PgPool;
pub async fn get_courses_for_tutor_db(
pool: &PgPool,
tutor_id: i32,
) -> Result<Vec<Course>, EzyTutorError> {
// Prepare SQL statement
let course_rows: Vec<Course> = sqlx::query_as!( #1
Course,
"SELECT * FROM ezy_course_c6 where tutor_id = $1",
tutor_id
)
.fetch_all(pool) #2
.await?; #3
Ok(course_rows) #4
}
In `src/iter5/dbaccess/course.rs`, `sqlx::query_as!` names `Course` as the result type, runs a tutor-filtered `SELECT`, and uses `fetch_all` to return `Vec<Course>`. The derived mapping avoids assigning each selected column to a struct field by hand. section 6.2.2
The same result-mapping pattern appears across course operations:
- Fetch one course with `fetch_optional`; return the `Course` when present or a not-found error when absent. section 6.2.2
- Insert a course with PostgreSQL `RETURNING` to obtain the new record as a `Course` in the same query. section 6.2.2
- Fetch the existing `Course` before an update so omitted optional update fields can retain their stored values. section 6.2.2
Automatic mapping is a convenience, not the only approach: database records can also be mapped to Rust structs manually when `query_as!` is unsuitable. The treatment uses the derive in application code rather than implementing the trait by hand. section 6.4 section 6.2.2

