Skip to content

Commit

Permalink
Add Axum+Diesel example (awslabs#629)
Browse files Browse the repository at this point in the history
  • Loading branch information
calavera authored Apr 8, 2023
1 parent 3888a1b commit cd0a19c
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 0 deletions.
23 changes: 23 additions & 0 deletions examples/http-axum-diesel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "http-axum-diesel"
version = "0.1.0"
edition = "2021"


# Use cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to manage dependencies.
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.

[dependencies]
axum = "0.6.4"
bb8 = "0.8.0"
diesel = "2.0.3"
diesel-async = { version = "0.2.1", features = ["postgres", "bb8"] }
lambda_http = { path = "../../lambda-http" }
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.159"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["ansi", "fmt"] }
13 changes: 13 additions & 0 deletions examples/http-axum-diesel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# AWS Lambda Function example

This example shows how to develop a REST API with Axum and Diesel that connects to a Postgres database.

## Build & Deploy

1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)
2. Build the function with `cargo lambda build --release`
3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE`

## Build for ARM 64

Build the function with `cargo lambda build --release --arm64`
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE posts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- Your SQL goes here
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
content TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT FALSE
)
122 changes: 122 additions & 0 deletions examples/http-axum-diesel/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use axum::{
extract::{Path, State},
response::Json,
routing::get,
Router,
};
use bb8::Pool;
use diesel::prelude::*;
use diesel_async::{pooled_connection::AsyncDieselConnectionManager, AsyncPgConnection, RunQueryDsl};
use lambda_http::{http::StatusCode, run, Error};
use serde::{Deserialize, Serialize};

table! {
posts (id) {
id -> Integer,
title -> Text,
content -> Text,
published -> Bool,
}
}

#[derive(Default, Queryable, Selectable, Serialize)]
struct Post {
id: i32,
title: String,
content: String,
published: bool,
}

#[derive(Deserialize, Insertable)]
#[diesel(table_name = posts)]
struct NewPost {
title: String,
content: String,
published: bool,
}

type AsyncPool = Pool<AsyncDieselConnectionManager<AsyncPgConnection>>;
type ServerError = (StatusCode, String);

async fn create_post(State(pool): State<AsyncPool>, Json(post): Json<NewPost>) -> Result<Json<Post>, ServerError> {
let mut conn = pool.get().await.map_err(internal_server_error)?;

let post = diesel::insert_into(posts::table)
.values(post)
.returning(Post::as_returning())
.get_result(&mut conn)
.await
.map_err(internal_server_error)?;

Ok(Json(post))
}

async fn list_posts(State(pool): State<AsyncPool>) -> Result<Json<Vec<Post>>, ServerError> {
let mut conn = pool.get().await.map_err(internal_server_error)?;

let posts = posts::table
.filter(posts::dsl::published.eq(true))
.load(&mut conn)
.await
.map_err(internal_server_error)?;

Ok(Json(posts))
}

async fn get_post(State(pool): State<AsyncPool>, Path(post_id): Path<i32>) -> Result<Json<Post>, ServerError> {
let mut conn = pool.get().await.map_err(internal_server_error)?;

let post = posts::table
.find(post_id)
.first(&mut conn)
.await
.map_err(internal_server_error)?;

Ok(Json(post))
}

async fn delete_post(State(pool): State<AsyncPool>, Path(post_id): Path<i32>) -> Result<(), ServerError> {
let mut conn = pool.get().await.map_err(internal_server_error)?;

diesel::delete(posts::table.find(post_id))
.execute(&mut conn)
.await
.map_err(internal_server_error)?;

Ok(())
}

fn internal_server_error<E: std::error::Error>(err: E) -> ServerError {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// this needs to be set to false, otherwise ANSI color codes will
// show up in a confusing manner in CloudWatch logs.
.with_ansi(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();

// Set up the database connection
let db_url = std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable");
let config = AsyncDieselConnectionManager::<AsyncPgConnection>::new(db_url);
let connection = Pool::builder()
.build(config)
.await
.expect("unable to establish the database connection");

// Set up the API routes
let posts_api = Router::new()
.route("/", get(list_posts).post(create_post))
.route("/:id", get(get_post).delete(delete_post));
let app = Router::new().nest("/posts", posts_api).with_state(connection);

run(app).await
}

0 comments on commit cd0a19c

Please sign in to comment.