Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
381 changes: 138 additions & 243 deletions examples/axum/Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion examples/axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ crate-type = ["cdylib"]
[dependencies]
worker = { version = "0.7", features = ['http', 'axum'] }
worker-macros = { version = "0.7", features = ['http'] }
axum = { version = "0.8", default-features = false }
axum = { version = "0.8", default-features = false, features = ['json'] }
axum-macros = "0.5.0"
tower-service = "0.3.3"
wasm-bindgen-futures = "0.4"
wasm-bindgen = "0.2.106"
serde = { version = "1.0", features = ["derive"] }
66 changes: 66 additions & 0 deletions examples/axum/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde::Serialize;

#[derive(Debug)]
pub enum AppError {
NotFound,
BadRequest(String),
Unauthorized,
Forbidden,
Internal(String),
}

impl From<worker::KvError> for AppError {
fn from(err: worker::KvError) -> Self {
AppError::Internal(err.to_string())
}
}

impl From<worker::Error> for AppError {
fn from(err: worker::Error) -> Self {
AppError::Internal(err.to_string())
}
}

#[derive(Serialize)]
struct Err {
msg: String,
}

impl IntoResponse for AppError {
fn into_response(self) -> Response {
match self {
AppError::NotFound => (
StatusCode::NOT_FOUND,
Json(Err {
msg: "not_found".into(),
}),
),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, Json(Err { msg })),
AppError::Unauthorized => (
StatusCode::UNAUTHORIZED,
Json(Err {
msg: "UNAUTHORIZED".into(),
}),
),
AppError::Forbidden => (
StatusCode::FORBIDDEN,
Json(Err {
msg: "FORBIDDEN".into(),
}),
),
AppError::Internal(_err) => (
// log the err or put into a tracing span!
StatusCode::INTERNAL_SERVER_ERROR,
Json(Err {
msg: "INTERNAL SERVER ERROR".into(),
}),
),
}
.into_response()
}
}
30 changes: 26 additions & 4 deletions examples/axum/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
use axum::{routing::get, Router};
use std::sync::Arc;
use tower_service::Service;
use worker::*;

fn router() -> Router {
Router::new().route("/", get(root))
pub mod error;
pub mod resources;

use crate::resources::foos::{self, service::FooService};

/// AppState's readonly fields are all `Arc<T>` for safe sharing between threads
#[derive(Clone)]
struct AppState {
foo_service: Arc<FooService>,
}

fn router(env: Env) -> Router {
let kv = env.kv("EXAMPLE").unwrap();
let foo_service = FooService::new(kv);

let app_state = AppState {
foo_service: Arc::new(foo_service),
};

Router::new()
.route("/", get(root))
.route("/foo", get(foos::api::get))
.with_state(app_state)
}

#[event(fetch)]
async fn fetch(
req: HttpRequest,
_env: Env,
env: Env,
_ctx: Context,
) -> Result<axum::http::Response<axum::body::Body>> {
Ok(router().call(req).await?)
Ok(router(env).call(req).await?)
}

pub async fn root() -> &'static str {
Expand Down
25 changes: 25 additions & 0 deletions examples/axum/src/resources/foos/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::error::AppError;
use crate::{resources::foos::model::Foo, AppState};
use axum::extract::{Path, State};
use axum::Json;
use axum_macros::debug_handler;
use worker::Result;

/// `get()` requires the `#[worker::send]` macro because Cloudflare Workers
/// execute a handler's future on a single JavaScript event loop.
///
/// The macro helps make `await` boundaries in the handler's function body `Send`
/// so the worker runtime can safely poll them.
///
/// You can read more about it here in the "`Send` Helpers" section:
/// https://docs.rs/worker/latest/worker/
#[worker::send]
#[debug_handler]
pub async fn get(
State(state): State<AppState>,
Path(foo_id): Path<String>,
) -> Result<Json<Foo>, AppError> {
let foo = state.foo_service.get(foo_id).await?;

foo.ok_or(AppError::NotFound).map(Json)
}
3 changes: 3 additions & 0 deletions examples/axum/src/resources/foos/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod api;
pub mod model;
pub mod service;
7 changes: 7 additions & 0 deletions examples/axum/src/resources/foos/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Foo {
pub id: String,
pub msg: String,
}
19 changes: 19 additions & 0 deletions examples/axum/src/resources/foos/service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::resources::foos::model::Foo;
use worker::{Result, KvStore, KvError};

pub struct FooService {
kv: KvStore
}

impl FooService {
pub fn new(kv: KvStore) -> FooService {
FooService {
kv
}
}

pub async fn get(&self, foo_id: String) -> Result<Option<Foo>, KvError> {
let maybe_foo = self.kv.get(&foo_id);
maybe_foo.json::<Foo>().await
}
}
1 change: 1 addition & 0 deletions examples/axum/src/resources/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod foos;
Loading