-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* adds axum-wasm feature * adds axum worker example --------- Co-authored-by: Dominik Lenz <[email protected]>
- Loading branch information
1 parent
8bd148a
commit 42d1b7c
Showing
16 changed files
with
482 additions
and
4 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
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
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
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,3 @@ | ||
node_modules/ | ||
build/ | ||
.wrangler/ |
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,32 @@ | ||
[package] | ||
name = "example-axum-worker" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
aide = { path = "../../crates/aide", features = [ | ||
"redoc", | ||
"scalar", | ||
"axum", | ||
"axum-extra", | ||
"macros", | ||
"axum-wasm" | ||
] } | ||
tower-service = "0.3.2" | ||
async-trait = "0.1.57" | ||
worker = { version = "0.1.0", features = ["http", "axum"] } | ||
console_error_panic_hook = "0.1.7" | ||
axum = { version = "0.7.1", default-features = false, features = ["macros", "form", "matched-path", "query", "original-uri"] } | ||
axum-extra = "0.9.0" | ||
axum-jsonschema = { path = "../../crates/axum-jsonschema", features = [ | ||
"aide", | ||
] } | ||
axum-macros = "0.4.0" | ||
schemars = { version = "0.8.10", features = ["uuid1"] } | ||
serde = { version = "1.0.144", features = ["derive", "rc"] } | ||
serde_json = "1.0.85" | ||
uuid = { version = "1.1.2", features = ["serde", "v4"] } |
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,5 @@ | ||
# Aide axum | ||
|
||
A minimal to-do axum cloudflare worker documented with aide. | ||
|
||
You can run it with `npm run dev`, and then visit the documentation at `http://localhost:3000`. |
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,11 @@ | ||
{ | ||
"name": "example-axum-worker", | ||
"version": "0.0.0", | ||
"private": true, | ||
"scripts": { | ||
"dev": "wrangler dev --env dev" | ||
}, | ||
"devDependencies": { | ||
"wrangler": "^3.49.0" | ||
} | ||
} |
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,6 @@ | ||
# Todo API | ||
|
||
A very simple Todo server with documentation. | ||
|
||
The purpose is to showcase the documentation workflow of Aide rather | ||
than a correct implementation. |
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,58 @@ | ||
use std::sync::Arc; | ||
|
||
use aide::{ | ||
axum::{ | ||
routing::{get, get_with}, | ||
ApiRouter, IntoApiResponse, | ||
}, | ||
openapi::OpenApi, | ||
redoc::Redoc, | ||
scalar::Scalar, | ||
}; | ||
use axum::{response::IntoResponse, Extension}; | ||
|
||
use crate::{extractors::Json, state::AppState}; | ||
|
||
pub fn docs_routes(state: AppState) -> ApiRouter { | ||
// We infer the return types for these routes | ||
// as an example. | ||
// | ||
// As a result, the `serve_redoc` route will | ||
// have the `text/html` content-type correctly set | ||
// with a 200 status. | ||
aide::gen::infer_responses(true); | ||
|
||
let router: ApiRouter = ApiRouter::new() | ||
.api_route_with( | ||
"/", | ||
get_with( | ||
Scalar::new("/docs/private/api.json") | ||
.with_title("Aide Axum") | ||
.axum_handler(), | ||
|op| op.description("This documentation page."), | ||
), | ||
|p| p.security_requirement("ApiKey"), | ||
) | ||
.api_route_with( | ||
"/redoc", | ||
get_with( | ||
Redoc::new("/docs/private/api.json") | ||
.with_title("Aide Axum") | ||
.axum_handler(), | ||
|op| op.description("This documentation page."), | ||
), | ||
|p| p.security_requirement("ApiKey"), | ||
) | ||
.route("/private/api.json", get(serve_docs)) | ||
.with_state(state); | ||
|
||
// Afterwards we disable response inference because | ||
// it might be incorrect for other routes. | ||
aide::gen::infer_responses(false); | ||
|
||
router | ||
} | ||
|
||
async fn serve_docs(Extension(api): Extension<Arc<OpenApi>>) -> impl IntoApiResponse { | ||
Json(api).into_response() | ||
} |
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,49 @@ | ||
use axum::{http::StatusCode, response::IntoResponse}; | ||
use schemars::JsonSchema; | ||
use serde::Serialize; | ||
use serde_json::Value; | ||
use uuid::Uuid; | ||
|
||
/// A default error response for most API errors. | ||
#[derive(Debug, Serialize, JsonSchema)] | ||
pub struct AppError { | ||
/// An error message. | ||
pub error: String, | ||
/// A unique error ID. | ||
pub error_id: Uuid, | ||
#[serde(skip)] | ||
pub status: StatusCode, | ||
/// Optional Additional error details. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub error_details: Option<Value>, | ||
} | ||
|
||
impl AppError { | ||
pub fn new(error: &str) -> Self { | ||
Self { | ||
error: error.to_string(), | ||
error_id: Uuid::new_v4(), | ||
status: StatusCode::BAD_REQUEST, | ||
error_details: None, | ||
} | ||
} | ||
|
||
pub fn with_status(mut self, status: StatusCode) -> Self { | ||
self.status = status; | ||
self | ||
} | ||
|
||
pub fn with_details(mut self, details: Value) -> Self { | ||
self.error_details = Some(details); | ||
self | ||
} | ||
} | ||
|
||
impl IntoResponse for AppError { | ||
fn into_response(self) -> axum::response::Response { | ||
let status = self.status; | ||
let mut res = axum::Json(self).into_response(); | ||
*res.status_mut() = status; | ||
res | ||
} | ||
} |
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,38 @@ | ||
use aide::operation::OperationIo; | ||
use axum::response::IntoResponse; | ||
use axum_jsonschema::JsonSchemaRejection; | ||
use axum_macros::FromRequest; | ||
use serde::Serialize; | ||
use serde_json::json; | ||
|
||
use crate::errors::AppError; | ||
|
||
#[derive(FromRequest, OperationIo)] | ||
#[from_request(via(axum_jsonschema::Json), rejection(AppError))] | ||
#[aide( | ||
input_with = "axum_jsonschema::Json<T>", | ||
output_with = "axum_jsonschema::Json<T>", | ||
json_schema | ||
)] | ||
pub struct Json<T>(pub T); | ||
|
||
impl<T> IntoResponse for Json<T> | ||
where | ||
T: Serialize, | ||
{ | ||
fn into_response(self) -> axum::response::Response { | ||
axum::Json(self.0).into_response() | ||
} | ||
} | ||
|
||
impl From<JsonSchemaRejection> for AppError { | ||
fn from(rejection: JsonSchemaRejection) -> Self { | ||
match rejection { | ||
JsonSchemaRejection::Json(j) => Self::new(&j.to_string()), | ||
JsonSchemaRejection::Serde(_) => Self::new("invalid request"), | ||
JsonSchemaRejection::Schema(s) => { | ||
Self::new("invalid request").with_details(json!({ "schema_validation": s })) | ||
} | ||
} | ||
} | ||
} |
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,84 @@ | ||
use std::sync::Arc; | ||
|
||
use aide::{ | ||
axum::ApiRouter, | ||
openapi::{OpenApi, Tag}, | ||
transform::TransformOpenApi, | ||
}; | ||
use axum::{http::StatusCode, Extension, http}; | ||
use tower_service::Service; | ||
use docs::docs_routes; | ||
use errors::AppError; | ||
use extractors::Json; | ||
use state::AppState; | ||
use todos::routes::todo_routes; | ||
use uuid::Uuid; | ||
use worker::{console_log, Context, Env, event, HttpRequest}; | ||
|
||
pub mod docs; | ||
pub mod errors; | ||
pub mod extractors; | ||
pub mod state; | ||
pub mod todos; | ||
|
||
#[event(start)] | ||
fn start(){ | ||
console_log!("Example docs are accessible at http://127.0.0.1:3000/docs"); | ||
} | ||
|
||
#[event(fetch)] | ||
async fn fetch( | ||
req: HttpRequest, | ||
_env: Env, | ||
_ctx: Context, | ||
) -> worker::Result<http::Response<axum::body::Body>> { | ||
console_error_panic_hook::set_once(); | ||
aide::gen::on_error(|error| { | ||
println!("{error}"); | ||
}); | ||
|
||
aide::gen::extract_schemas(true); | ||
|
||
|
||
let state = AppState::default(); | ||
|
||
let mut api = OpenApi::default(); | ||
|
||
let mut app = ApiRouter::new() | ||
.nest_api_service("/todo", todo_routes(state.clone())) | ||
.nest_api_service("/docs", docs_routes(state.clone())) | ||
.finish_api_with(&mut api, api_docs) | ||
.layer(Extension(Arc::new(api))) // Arc is very important here or you will face massive memory and performance issues | ||
.with_state(state); | ||
|
||
Ok(app.call(req).await?) | ||
} | ||
|
||
fn api_docs(api: TransformOpenApi) -> TransformOpenApi { | ||
api.title("Aide axum Open API") | ||
.summary("An example Todo application") | ||
.description(include_str!("README.md")) | ||
.tag(Tag { | ||
name: "todo".into(), | ||
description: Some("Todo Management".into()), | ||
..Default::default() | ||
}) | ||
.security_scheme( | ||
"ApiKey", | ||
aide::openapi::SecurityScheme::ApiKey { | ||
location: aide::openapi::ApiKeyLocation::Header, | ||
name: "X-Auth-Key".into(), | ||
description: Some("A key that is ignored.".into()), | ||
extensions: Default::default(), | ||
}, | ||
) | ||
.default_response_with::<Json<AppError>, _>(|res| { | ||
res.example(AppError { | ||
error: "some error happened".to_string(), | ||
error_details: None, | ||
error_id: Uuid::nil(), | ||
// This is not visible. | ||
status: StatusCode::IM_A_TEAPOT, | ||
}) | ||
}) | ||
} |
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 @@ | ||
use std::{ | ||
collections::HashMap, | ||
sync::{Arc, Mutex}, | ||
}; | ||
|
||
use uuid::Uuid; | ||
|
||
use crate::todos::TodoItem; | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct AppState { | ||
pub todos: Arc<Mutex<HashMap<Uuid, TodoItem>>>, | ||
} |
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,15 @@ | ||
use schemars::JsonSchema; | ||
use serde::{Deserialize, Serialize}; | ||
use uuid::Uuid; | ||
|
||
pub mod routes; | ||
|
||
/// A single Todo item. | ||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] | ||
pub struct TodoItem { | ||
pub id: Uuid, | ||
/// The description of the item. | ||
pub description: String, | ||
/// Whether the item was completed. | ||
pub complete: bool, | ||
} |
Oops, something went wrong.