Implement server endpoints
This commit is contained in:
142
src/main.rs
142
src/main.rs
@ -1,26 +1,146 @@
|
||||
use axum::{
|
||||
Router,
|
||||
Json, Router,
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, post},
|
||||
};
|
||||
use sqlx::PgPool;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, sqlx::FromRow)]
|
||||
struct Sandwich {
|
||||
id: i32,
|
||||
name: String,
|
||||
composition: String,
|
||||
price: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct CreateSandwich {
|
||||
name: String,
|
||||
composition: String,
|
||||
price: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct SandwichPathParams {
|
||||
id: i32,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let router = Router::new().nest(
|
||||
"/sandwiches",
|
||||
Router::new()
|
||||
.route("/", get(handler))
|
||||
.route("/", post(handler))
|
||||
.route("/{id}", post(handler))
|
||||
.route("/{id}", delete(handler)),
|
||||
);
|
||||
let _ = dotenvy::dotenv();
|
||||
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL should be set");
|
||||
println!("DATABASE_URL={database_url}");
|
||||
|
||||
let pool = PgPool::connect(&database_url)
|
||||
.await
|
||||
.expect("Could not connect to database");
|
||||
sqlx::raw_sql(
|
||||
r#"
|
||||
-- DROP TABLE IF EXISTS sandwiches;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sandwiches(
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
composition TEXT NOT NULL,
|
||||
price INTEGER NOT NULL
|
||||
);
|
||||
"#,
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("Could not create database tables");
|
||||
let state = AppState { pool };
|
||||
let router = Router::new()
|
||||
.nest(
|
||||
"/sandwiches",
|
||||
Router::new()
|
||||
.route("/", get(get_sandwiches))
|
||||
.route("/", post(post_sandwiches))
|
||||
.route("/{id}", get(get_sandwich_by_id))
|
||||
.route("/{id}", post(post_sandwich_by_id))
|
||||
.route("/{id}", delete(delete_sandwich_by_id)),
|
||||
)
|
||||
.with_state(state);
|
||||
let addr = "0.0.0.0:8000";
|
||||
let listener = TcpListener::bind(addr).await.unwrap();
|
||||
println!("Listener at {addr}");
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
}
|
||||
|
||||
async fn handler() -> impl IntoResponse {
|
||||
axum::http::StatusCode::NOT_IMPLEMENTED
|
||||
async fn post_sandwiches(
|
||||
State(state): State<AppState>,
|
||||
Json(sandwich): Json<CreateSandwich>,
|
||||
) -> impl IntoResponse {
|
||||
sqlx::query("INSERT INTO sandwiches(name, composition, price) VALUES($1, $2, $3)")
|
||||
.bind(sandwich.name)
|
||||
.bind(sandwich.composition)
|
||||
.bind(sandwich.price)
|
||||
.execute(&state.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
StatusCode::CREATED
|
||||
}
|
||||
|
||||
async fn get_sandwiches(State(state): State<AppState>) -> impl IntoResponse {
|
||||
let sanwiches: Vec<Sandwich> = sqlx::query_as("SELECT * FROM sandwiches")
|
||||
.fetch_all(&state.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Json(sanwiches)
|
||||
}
|
||||
|
||||
async fn get_sandwich_by_id(
|
||||
State(state): State<AppState>,
|
||||
Path(params): Path<SandwichPathParams>,
|
||||
) -> impl IntoResponse {
|
||||
let sandwich: Option<Sandwich> = sqlx::query_as("SELECT * FROM sandwiches WHERE id = $1")
|
||||
.bind(params.id)
|
||||
.fetch_optional(&state.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
match sandwich {
|
||||
Some(s) => Json(s).into_response(),
|
||||
None => StatusCode::NOT_FOUND.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn post_sandwich_by_id(
|
||||
State(state): State<AppState>,
|
||||
Path(params): Path<SandwichPathParams>,
|
||||
Json(sandwich): Json<CreateSandwich>,
|
||||
) -> impl IntoResponse {
|
||||
sqlx::query("UPDATE sandwiches SET name = $1, composition = $2, price = $3 WHERE id = $4")
|
||||
.bind(sandwich.name)
|
||||
.bind(sandwich.composition)
|
||||
.bind(sandwich.price)
|
||||
.bind(params.id)
|
||||
.execute(&state.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
StatusCode::CREATED
|
||||
}
|
||||
|
||||
async fn delete_sandwich_by_id(
|
||||
State(state): State<AppState>,
|
||||
Path(params): Path<SandwichPathParams>,
|
||||
) -> impl IntoResponse {
|
||||
sqlx::query("DELETE FROM sandwiches WHERE id = $1")
|
||||
.bind(params.id)
|
||||
.execute(&state.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
StatusCode::NO_CONTENT
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user