forked from awslabs/aws-lambda-rust-runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Axum+Diesel example (awslabs#629)
- Loading branch information
Showing
5 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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` |
2 changes: 2 additions & 0 deletions
2
examples/http-axum-diesel/migrations/2023-04-07-231632_create_posts/down.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
7 changes: 7 additions & 0 deletions
7
examples/http-axum-diesel/migrations/2023-04-07-231632_create_posts/up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |