Sitemap

Warp | Rest Api | Sqlite | Seaorm

4 min readOct 27, 2026

--

We will build a basic to-do list api with warp , sqlite and sea-orm .

(Youtube Video)

1

let’s add dependencies in cargo.toml :

[dependencies]
tokio = { version = "1", features = ["full"] }
warp = { version = "0.4", features=["server"]}

serde = {version = "1.0.219", features = ["derive"]}
serde_json = "1.0.140"

sea-orm = { version = "2.0.0-rc", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros" ] }

We add warp for backend server , serde and serde_json for serialize and deserialize json . sea-orm for interact with sqlite database. tokio for async runtime.

2.

We create a new file named todo.rs , we add this file as module to our project in main.rs .

In todo.rs:

use sea_orm::prelude::*;
use serde::Serialize;

#[sea_orm::model]
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel, Default, Serialize)]
#[sea_orm(table_name = "todos")]
pub struct Model {
#[sea_orm(primary_key)]
id: i32,
content: String
}

impl ActiveModelBehavior for ActiveModel {

}

We define a Model for todo item in todo.rs

In main.rs :

3

#[derive(Deserialize)]
struct TodoReq {
content:String
}

We define a struct type for get json data from user request body.

4

async fn db() -> DatabaseConnection {
let conn = Database::connect("sqlite://db.sqlite").await.unwrap();

let db_backend = conn.get_database_backend();
let stat = Statement::from_string(db_backend, "
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT
)
");
// execute raw
conn.execute_raw(stat).await.unwrap();
// return db connection
conn
}

We define an async function to connect to sqlite database , also create a table named todos in database.

5

async fn get_todo_list(conn: DatabaseConnection) -> Result<warp::reply::Json , warp::Rejection> {
let todos = TodoEntity::find().all(&conn).await.unwrap();
let todos_json = serde_json::to_string_pretty(&todos).unwrap();
Ok(warp::reply::json(&todos_json))
}

We define a handler to get all todo items from database.

Get Mike Code’s stories in your inbox

Join Medium for free to get updates from this writer.

6

async fn add_todo(todo_req: TodoReq, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo = TodoActiveModel {
content: Set(todo_req.content.clone()),
..Default::default()
};
todo.insert(&conn).await.unwrap();

Ok("Add new Todo Successful.".to_string() )
}

We define a handler to add new todo item to database.

7

async fn get_single_todo(id: i32, conn: DatabaseConnection) -> Result<warp::reply::Json , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(warp::reply::json(&msg))
},
Some(todo) => {
let todo_json = serde_json::to_string_pretty(&todo).unwrap();
Ok(warp::reply::json(&todo_json))
}
}
}

We define a handler to get one specific todo item by id.

8

async fn delete_todo(id: i32, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(msg)
},
Some(todo) => {
todo.delete(&conn).await.unwrap();
let msg = format!("Delete Todo id : {} Successful", id);
Ok(msg)
}
}
}

We define a handler to delete one specific todo by id.

9

async fn update_todo(id: i32, todo_req: TodoReq, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(msg)
},
Some(todo) => {
let mut todo = todo.into_active_model();
todo.content = Set(todo_req.content.clone());
todo.update(&conn).await.unwrap();
let msg = format!("Update Todo id : {} Successful", id);
Ok(msg)
}
}
}

We define a handler to update a todo item by id .

10

async fn app(conn: DatabaseConnection) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
let root = warp::path!().map(|| "Home");

let state_filter = warp::any().map(move || conn.clone());

let todo_list_route = warp::path!("todo_list").and(warp::get()).and(state_filter.clone()).and_then(get_todo_list);

let add_todo_route = warp::path!("add_todo").and(warp::post()).and(warp::body::json()).and(state_filter.clone()).and_then(add_todo);

let single_todo_route = warp::path!("todo" / i32).and(warp::get()).and(state_filter.clone()).and_then(get_single_todo);

let delete_todo_route = warp::path!("delete_todo" / i32 ).and(warp::delete()).and(state_filter.clone()).and_then(delete_todo);

let update_todo_route = warp::path!("update_todo" / i32).and(warp::put()).and(warp::body::json()).and(state_filter.clone()).and_then(update_todo);

root
.or(todo_list_route)
.or(add_todo_route)
.or(single_todo_route)
.or(delete_todo_route)
.or(update_todo_route)
}

We define app function to create a router , and register all routes with handlers and database connection as shared state.

11

#[tokio::main]
async fn main() {
let conn = db().await;
let app = app(conn).await;

warp::serve(app)
.run(([0,0,0,0], 3000))
.await;
}

In main function ,we serve the app .

Full code in main.rs

use sea_orm::{ActiveValue::Set, Database, DatabaseConnection, IntoActiveModel, Statement, prelude::*};
use serde::Deserialize;
use warp::Filter;
use todo::{Entity as TodoEntity, ActiveModel as TodoActiveModel};
mod todo;

#[tokio::main]
async fn main() {
let conn = db().await;
let app = app(conn).await;

warp::serve(app)
.run(([0,0,0,0], 3000))
.await;
}
//
async fn app(conn: DatabaseConnection) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
let root = warp::path!().map(|| "Home");

let state_filter = warp::any().map(move || conn.clone());

let todo_list_route = warp::path!("todo_list").and(warp::get()).and(state_filter.clone()).and_then(get_todo_list);

let add_todo_route = warp::path!("add_todo").and(warp::post()).and(warp::body::json()).and(state_filter.clone()).and_then(add_todo);

let single_todo_route = warp::path!("todo" / i32).and(warp::get()).and(state_filter.clone()).and_then(get_single_todo);

let delete_todo_route = warp::path!("delete_todo" / i32 ).and(warp::delete()).and(state_filter.clone()).and_then(delete_todo);

let update_todo_route = warp::path!("update_todo" / i32).and(warp::put()).and(warp::body::json()).and(state_filter.clone()).and_then(update_todo);

root
.or(todo_list_route)
.or(add_todo_route)
.or(single_todo_route)
.or(delete_todo_route)
.or(update_todo_route)
}

//
async fn get_todo_list(conn: DatabaseConnection) -> Result<warp::reply::Json , warp::Rejection> {
let todos = TodoEntity::find().all(&conn).await.unwrap();
let todos_json = serde_json::to_string_pretty(&todos).unwrap();
Ok(warp::reply::json(&todos_json))
}
//
async fn add_todo(todo_req: TodoReq, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo = TodoActiveModel {
content: Set(todo_req.content.clone()),
..Default::default()
};
todo.insert(&conn).await.unwrap();

Ok("Add new Todo Successful.".to_string() )
}

//
async fn get_single_todo(id: i32, conn: DatabaseConnection) -> Result<warp::reply::Json , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(warp::reply::json(&msg))
},
Some(todo) => {
let todo_json = serde_json::to_string_pretty(&todo).unwrap();
Ok(warp::reply::json(&todo_json))
}
}
}

//
async fn update_todo(id: i32, todo_req: TodoReq, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(msg)
},
Some(todo) => {
let mut todo = todo.into_active_model();
todo.content = Set(todo_req.content.clone());
todo.update(&conn).await.unwrap();
let msg = format!("Update Todo id : {} Successful", id);
Ok(msg)
}
}
}
//
async fn delete_todo(id: i32, conn: DatabaseConnection) -> Result<String , warp::Rejection> {

let todo_op = TodoEntity::find_by_id(id).one(&conn).await.unwrap();
match todo_op {
None => {
let msg = format!("No todo id : {} Found", id);
Ok(msg)
},
Some(todo) => {
todo.delete(&conn).await.unwrap();
let msg = format!("Delete Todo id : {} Successful", id);
Ok(msg)
}
}
}


//
async fn db() -> DatabaseConnection {
let conn = Database::connect("sqlite://db.sqlite").await.unwrap();
let db_backend = conn.get_database_backend();
let stat = Statement::from_string(db_backend, "
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT
)
");
conn.execute(stat).await.unwrap();
conn
}

//
#[derive(Deserialize)]
struct TodoReq {
content:String
}

--

--