Skip to content

Latest commit

 

History

History
151 lines (123 loc) · 4 KB

File metadata and controls

151 lines (123 loc) · 4 KB
title og:title description
Postgres Todo App
Postgres Todo App with Actix Web - Shuttle Docs
This article walks you through how you can easily set up a simple to-do app using Actix Web and SQLx with PostgresQL.

Description

This example shows how to make a simple TODO app using Actix Web and a shared Shuttle Postgres DB.

The following routes are provided:

  • GET /todos/<id> - Get a to-do item by ID.
  • POST /todos - Create a to-do item. Takes "note" as a JSON body parameter.

You can clone the example below by running the following (you'll need shuttle CLI installed):

shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/postgres

Code

```rust src/main.rs use actix_web::middleware::Logger; use actix_web::{ error, get, post, web::{self, Json, ServiceConfig}, Result, }; use serde::{Deserialize, Serialize}; use shuttle_actix_web::ShuttleActixWeb; use shuttle_runtime::{CustomError}; use sqlx::{Executor, FromRow, PgPool};

#[get("/{id}")] async fn retrieve(path: web::Path, state: web::Data) -> Result<Json> { // query database to get data // if error, return Bad Request HTTP status code let todo = sqlx::query_as("SELECT * FROM todos WHERE id = $1") .bind(*path) .fetch_one(&state.pool) .await .map_err(|e| error::ErrorBadRequest(e.to_string()))?;

Ok(Json(todo))

}

#[post("")] async fn add(todo: web::Json, state: web::Data) -> Result<Json> { // query database to create a new record using the request body // if error, return Bad Request HTTP status code let todo = sqlx::query_as("INSERT INTO todos(note) VALUES ($1) RETURNING id, note") .bind(&todo.note) .fetch_one(&state.pool) .await .map_err(|e| error::ErrorBadRequest(e.to_string()))?;

Ok(Json(todo))

}

#[derive(Clone)] struct AppState { pool: PgPool, }

#[shuttle_runtime::main] async fn actix_web( #[shuttle_shared_db::Postgres] pool: PgPool, ) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> { // run migrations pool.execute(include_str!("../schema.sql")) .await .map_err(CustomError::new)?;

// set up AppState let state = web::Data::new(AppState { pool });

// set up our Actix web service and wrap it with logger and add the AppState as app data let config = move |cfg: &mut ServiceConfig| { cfg.service( web::scope("/todos") .wrap(Logger::default()) .service(retrieve) .service(add) .app_data(state), ); };

Ok(config.into())

}

#[derive(Deserialize)] struct TodoNew { pub note: String, }

#[derive(Serialize, Deserialize, FromRow)] struct Todo { pub id: i32, pub note: String, }


```sql schema.sql
DROP TABLE IF EXISTS todos;

CREATE TABLE todos (
  id serial PRIMARY KEY,
  note TEXT NOT NULL
);
[package]
name = "postgres"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.3.1"
shuttle-actix-web = "0.57.0"
shuttle-runtime = "0.57.0"
serde = "1.0.148"
shuttle-shared-db = { version = "0.57.0", features = ["postgres", "sqlx"] }
sqlx = "0.8.2"
tokio = "1.26.0"

Usage

Once you've cloned the example, try launching it locally using shuttle run. Once you've verified that it runs successfully, try using cURL in a new terminal to send a POST request:

curl -X POST -d '{"note":"Hello world!"}' -H 'Content-Type: application/json' \
 http://localhost:8000/todo

Assuming the request was successful, you'll get back a JSON response with the ID and Note of the record you just created. If you try the following cURL command, you should be able to then retrieve the message you stored:

curl http://localhost:8000/todo/<id>

Interested in extending this example? Here's as couple of ideas:

  • Add update and delete routes
  • Add static files to show your records