- Examples
- Poem
Examples
Poem
A full-featured and easy-to-use web framework with the Rust programming language.
Hello world!
Simple ‘Hello world’ app using Poem.
Create a new directory (mkdir
) and move into it (cd
) — afterwards, execute the following command to initialize shuttle inside with the Poem boilerplate.
cargo shuttle init --poem
Make sure that your Cargo.toml
file looks like the one below — having the right dependencies is key!
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"
[lib]
[dependencies]
poem = "1.3.35"
shuttle-service = { version = "0.9.0", features = ["web-poem"] }
Your lib.rs
should look like this:
use poem::{get, handler, Route};
#[handler]
fn hello_world() -> &'static str {
"Hello, world!"
}
#[shuttle_service::main]
async fn main() -> shuttle_service::ShuttlePoem<impl poem::Endpoint> {
let app = Route::new().at("/hello", get(hello_world));
Ok(app)
}
Create a project, this will start an isolated deployer container for you under the hood:
cargo shuttle project new
Finally, to deploy your app, all you need to do is:
cargo shuttle deploy
And your app is live! 🎉🎉🎉
Todo App (MongoDB)
Simple ‘todo app’ example using MongoDB.
Create a new directory (mkdir
) and move into it (cd
) — afterwards, execute the following command to initialize shuttle inside with the Poem boilerplate.
cargo shuttle init --poem
Make sure that your Cargo.toml
file looks like the one below — having the right dependencies is key!
[package]
name = "mongodb-poem-app"
version = "0.1.0"
edition = "2021"
[dependencies]
mongodb = "2.3.0"
poem = "1.3.35"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
shuttle-service = { version = "0.9.0", features = ["web-poem"] }
shuttle-shared-db = { version = "0.9.0", features = ["mongodb"] }
Your lib.rs
should look like this:
use mongodb::bson::doc;
use mongodb::bson::oid::ObjectId;
use mongodb::{Collection, Database};
use poem::{
error::{BadRequest, NotFoundError},
get, handler,
middleware::AddData,
post,
web::{Data, Json},
EndpointExt, FromRequest, Request, RequestBody, Result, Route,
};
use serde::{Deserialize, Serialize};
struct ObjectIdGuard(ObjectId);
#[poem::async_trait]
impl<'a> FromRequest<'a> for ObjectIdGuard {
async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result<Self> {
let id = req.path_params::<String>()?;
let obj_id = ObjectId::parse_str(id).map_err(BadRequest)?;
Ok(ObjectIdGuard(obj_id))
}
}
#[handler]
async fn retrieve(
ObjectIdGuard(id): ObjectIdGuard,
collection: Data<&Collection<Todo>>,
) -> Result<Json<serde_json::Value>> {
let filter = doc! {"_id": id};
let todo = collection
.find_one(filter, None)
.await
.map_err(BadRequest)?;
match todo {
Some(todo) => Ok(Json(serde_json::json!(todo))),
None => Err(NotFoundError.into()),
}
}
#[handler]
async fn add(Json(todo): Json<Todo>, collection: Data<&Collection<Todo>>) -> Result<String> {
let todo_id = collection
.insert_one(todo, None)
.await
.map_err(BadRequest)?;
Ok(todo_id
.inserted_id
.as_object_id()
.expect("id is objectId")
.to_string())
}
#[shuttle_service::main]
async fn main(
#[shuttle_shared_db::MongoDb] db: Database,
) -> shuttle_service::ShuttlePoem<impl poem::Endpoint> {
let collection = db.collection::<Todo>("todos");
let app = Route::new()
.at("/todo", post(add))
.at("/todo/:id", get(retrieve))
.with(AddData::new(collection));
Ok(app)
}
#[derive(Debug, Serialize, Deserialize)]
struct Todo {
pub note: String,
}
Create a project, this will start an isolated deployer container for you under the hood:
cargo shuttle project new
Finally, to deploy your app, all you need to do is:
cargo shuttle deploy
And your app is live! 🎉🎉🎉
Todo App (Postgres)
Simple ‘todo app’ example using Postgres.
Create a new directory (mkdir
) and move into it (cd
) — afterwards, execute the following command to initialize shuttle inside with the Poem boilerplate.
cargo shuttle init --poem
Make sure that your Cargo.toml
file looks like the one below — having the right dependencies is key!
[package]
name = "postgres"
version = "0.1.0"
edition = "2021"
[lib]
[dependencies]
poem = "1.3.35"
serde = "1.0"
shuttle-service = { version = "0.9.0", features = ["web-poem"] }
shuttle-shared-db = { version = "0.9.0", features = ["postgres"] }
sqlx = { version = "0.6", features = ["runtime-tokio-native-tls", "postgres"] }
Your lib.rs
should look like this:
use poem::{
error::BadRequest,
get, handler,
middleware::AddData,
post,
web::{Data, Json, Path},
EndpointExt, Result, Route,
};
use serde::{Deserialize, Serialize};
use shuttle_service::error::CustomError;
use sqlx::{Executor, FromRow, PgPool};
#[handler]
async fn retrieve(Path(id): Path<i32>, state: Data<&PgPool>) -> Result<Json<Todo>> {
let todo = sqlx::query_as("SELECT * FROM todos WHERE id = $1")
.bind(id)
.fetch_one(state.0)
.await
.map_err(BadRequest)?;
Ok(Json(todo))
}
#[handler]
async fn add(Json(data): Json<TodoNew>, state: Data<&PgPool>) -> Result<Json<Todo>> {
let todo = sqlx::query_as("INSERT INTO todos(note) VALUES ($1) RETURNING id, note")
.bind(&data.note)
.fetch_one(state.0)
.await
.map_err(BadRequest)?;
Ok(Json(todo))
}
#[shuttle_service::main]
async fn main(
#[shuttle_shared_db::Postgres] pool: PgPool,
) -> shuttle_service::ShuttlePoem<impl poem::Endpoint> {
pool.execute(include_str!("../schema.sql"))
.await
.map_err(CustomError::new)?;
let app = Route::new()
.at("/todo", post(add))
.at("/todo/:id", get(retrieve))
.with(AddData::new(pool));
Ok(app)
}
#[derive(Deserialize)]
struct TodoNew {
pub note: String,
}
#[derive(Serialize, FromRow)]
struct Todo {
pub id: i32,
pub note: String,
}
And your schema.sql
like this:
DROP TABLE IF EXISTS todos;
CREATE TABLE todos (
id serial PRIMARY KEY,
note TEXT NOT NULL
);
Finally, to deploy your app, all you need to do is:
cargo shuttle deploy
And your app is live! 🎉🎉🎉