#axum

154 messages · Page 1 of 1 (latest)

blazing nova

it usually has breaking changes

distant steeple
blazing nova

I mean you can use the main branch if you are not using any other crates in the axum subspace

but if you do, and they break because of these breaking changes, it would be unwise to approach their developers about it

distant steeple

Idk what's wrong with with me this weekend owo

blazing nova

You seem to be suffering from a mild case of eckdeetation for starters

But in general the reason it’s recommended to use a stable release is that main is bound to be inconsistent and may have further changes leading up to the next release, meaning potentially wasted time when patching dependent crates

distant steeple
blazing nova
blazing nova

Makes the sufferer of the disease appear unprofessional and generally places them on the ‘most likely under 14’ scale

distant steeple
distant steeple

Anyone has any idea why are symbols become undefined when I make axum or other crates as dylib?
Example

= note: wild: error: Undefined symbol aws_lc_0_37_1_ECDSA_SIG_get0_r, referenced by
              target/debug/deps/libjsonwebtoken-3d9c9373e20257c9.rlib @ jsonwebtoken-3d9c9373e20257c9.jsonwebtoken.bab6b9b0e81178a1-cgu.040.rcgu.o

Maybe I'll try different linker or something :/

blazing nova

That looks like TLS

grave frigate

Could you advise on how to build an architecture with Axum? Or maybe there are some resources I could read?

livid trail
brave eagle

if anybody is interested I've just released a new version of axum-oidc-client with two-tier authentication cache. Redis is no longer mandatory

distant steeple
blazing nova

you could also ask ‘can tower work with different async runtimes’ just as well

spiral river
distant steeple
distant steeple
blazing nova

The official answer is

axum is designed to work with tokio and hyper. Runtime and transport layer independence is not a goal, at least for the time being.

You could probably run it the way you’d do for wasm

spiral river

there also probably isn't a great reason to run axum with compio

distant steeple
dawn wedge
spiral river

and if you aren't doing fs ops, kqueue/epoll/(mio AFD) is very very likely sufficient

dawn wedge

hyper is runtime agnostic, but you'll have to implement a couple traits
writing your own accept loop is kinda annoying but possible

cosmic minnow

Is there (or if not, can someone recommend please) a recommended reading order for these examples?

dawn wedge
fierce ermine

Are there any examples around of uploading a file in multiple parts to axum and reassembling it?

fierce ermine
blazing nova

Uploading in multiple parts is probably an out of scope thing, like, you just need to combine stuff and how is axum supposed to be involved in that?

It’s not a feature in HTTP

I’d look for generic examples, not axum specific

fierce ermine

Didn't find anything useful.

blazing nova

It’s not an easy problem if you look at it from edge cases/security points of view

fierce ermine

Precisely why I was looking for examples.

blazing nova

What I saw in the wild in the 90s’ web was using existing libraries and tools for splitting files

Because that was important when you dealt in floppies and Zmodem

fierce ermine

I've seen mentioned quite often that uploading multiple chunks in parallel can be faster than a single serial upload.

spiral river

though s3 is also quite complicated, so it might be kind of difficult to understand

fierce ermine
blazing nova

Or unless you’re uploading to multiple different endpoints at once and they have bandwidth issues

You generally should avoid this approach on ADSL and DOCSIS links (and on mobile data)

Due to bin sharing

Parallel uploads mean higher PPS which increases latency on asymmetrical links with bin sharing

fierce ermine

I had suspected it wasn't obviously better, given how rare it seems. I'm presently stuck in a situation where I need to implement this to show someone else the results from trying it.

spiral river

one reason it might be better to chunk in some way is if you have a very unreliable connection

fierce ermine

right, because it limits what you need to retry

blazing nova
weary tulip

Hi everyone, I wrote a tool(https://github.com/xidl/xidl) that can generate axum scaffolding from IDL, is anyone interested?

What it does is generate some scaffolding from IDL. Users can generate routes by implementing the corresponding trait. In addition, it also supports generating openapi and jsonrpc. If anyone is interested, welcome to star!

Here is the actual effect:

use xidlc_examples::hello_world::HelloWorld;
use xidlc_examples::hello_world::HelloWorldSayHelloRequest;
use xidlc_examples::hello_world::HelloWorldServer;

struct HelloWorldImpl;

#[async_trait::async_trait]
impl HelloWorld for HelloWorldImpl {
    async fn sayHello(
        &self,
        req: xidl_rust_axum::Request<HelloWorldSayHelloRequest>,
    ) -> Result<(), xidl_rust_axum::Error> {
        let HelloWorldSayHelloRequest { name } = req.data;
        println!("Hello, {}!", name);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "127.0.0.1:3000";
    println!("axum hello_world server listening on {addr}");

    xidl_rust_axum::Server::builder()
        .with_service(HelloWorldServer::new(HelloWorldImpl))
        .serve(addr)
        .await?;

    Ok(())
}

Here is a simple example:

#progma xidlc package Smart City Public APIs
#progma xidlc version v2.0.0

interface SmartCityHttpApi {
    @get(path = "/v1/stops/{stop_id}{?line,lang}")
    string get_stop_eta(
        string stop_id,
        string line,
        string locale,
        out uint32 eta_seconds,
        out string destination
    );

    @post(path = "/v1/parking/lots/{lot_id}/reserve")
    string reserve_lot(
        @path("lot_id") string lot_id,
        string plate_number,
        uint32 minutes,
        out string reservation_state,
        out string expires_at
    );
    readonly attribute string api_version;
    attribute boolean maintenance_mode;
};

You can play it with https://xidl.github.io/xidl or install by cargo install xidlc

distant steeple
gilded elm

Hi, @everyone
I am building Rust Axum backend for Solana trading platform.
I used Postgresql as database but orm implementation was the difficult.
I used sea-orm but its a little confusing.
Who has expertise in sea-orm integration especially migration part?

blazing nova

This is entirely unrelated to axum so you are probably better off finding the SeaORM community resources

I find that a lot of people who go for SeaORM do it based on previous ‘experience’ with stuff like Django and often generally have trouble with documentation and lower level concepts (usually they are also crypt0-curr3ncy shills)

gilded elm
timber abyss

Hey all! I've created a very small PR extending Axum's documentation. If any of you could review it and give me feedback, that would be very helpful and welcome. Here is a link to the PR: https://github.com/tokio-rs/axum/pull/3683

Thanks in advance!

vapid crow

hi, I am implementing a container registry using axum, and the specification needs to support slashes in the repository name (e.g. user/repo)
so I tried to do .route("/{*repository}/blobs/{digest}", _)but it panicked with the error Catch-all parameters are only allowed at the end of a route.
how should I go about doing this

dawn wedge
vapid crow
dawn wedge
vapid crow

🥲

stuck steeple

bit offtopic,

I want to have constants for my routes like:

   pub const ADMIN: &str = "/admin";
   pub const ADMIN_DASHBOARD: &str = "/admin/dashboard";
   pub const ADMIN_PASSWORD: &str = "/admin/password";

but this is making composing things hard.
concat! macro doesn't work either.

ps: i am doing this and not typed path from axum-extras because:

  1. I want to see at one glance all my routes
  2. typed path dont have the ability to chain different HTTP method like: .route(LOGIN, get(login_form).post(login))
  3. constants can be used in other context like Redirect
tawny urchin
stuck steeple bit offtopic, I want to have constants for my `routes` like: ```rust pub c...

I've been wanting something similar and have settled with using typed paths for now. At least for case 3, you can do Redirect::to(&MyTypedPath{id: 1}.to_string()). But it's very clunky still and doesn't use rust's type system. Perhaps a TypedRedirect::to is needed?

Has anyone ever brought up the idea of adding an enum-based router to axum? I see this for example, but it uses a macro: https://crates.io/crates/axum-routes

vital pilot

What do you guys think about this WithRejection extractor that maps the rejection type without the use of PhandomData like the one in axum-extra

trait Foo<R> {
    type Inner;
    fn into_inner(self) -> Self::Inner;
}

impl<R, T> Foo<R> for T {
    type Inner = T;

    fn into_inner(self) -> Self::Inner {
        self
    }
}

struct WithRejection<I: Foo<R>, R>(I::Inner);

impl<S, I, R> FromRequestParts<S> for WithRejection<I, R>
where
    I: Foo<R> + FromRequestParts<S>,
    R: IntoResponse + From<I::Rejection>,
    S: Sync,
{
    type Rejection = R;

    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        I::from_request_parts(parts, state)
            .await
            .map_err(Into::into)
            .map(I::into_inner)
            .map(WithRejection)
    }
}

impl<S, I, R> FromRequest<S> for WithRejection<I, R>
where
    I: Foo<R> + FromRequest<S>,
    R: IntoResponse + From<I::Rejection>,
    S: Sync,
{
    type Rejection = R;

    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        I::from_request(req, state)
            .await
            .map_err(Into::into)
            .map(I::into_inner)
            .map(WithRejection)
    }
}

async fn fetch(WithRejection(Json(stuff)): WithRejection<Json<Stuff>, Error>) {}
weary tulip

I wrote a very interesting lsp. 🙂

you can install it by this id: cathaysia.vscode-idl-language
and here is a simple testcase:

interface HttpServer {
    /// host name
    attribute string host;
    /// port number
    readonly attribute uint16 port;
    // Get user info
    @get(path = "/v1/user/{id}")
    UserInfo get_user_info(
        uint64 id
    );
};
dawn wedge

what does that have to do with lsp? or axum?

weary tulip

HttpServer can be used to generate axum-based code (client and server).

I just wanted to show you this code lens, I think it very useful.

stuck steeple
brave eagle

Hello, I'd like to inform you that if you are interested I've just released the new version of https://crates.io/crates/axum-oidc-client that supports SQL based 2nd level cache (postgres, mysql and sqlite) and OIDC autodiscovery configuration. I've also written an exaustive set of examples. Any feedback is appreciated

jolly cave

Hey folks, new to Rust and Axum
I'm looking for the idiomatic way to transform an axum::http::Uri. For instance, I'd like to do something like uri.add_query_param("success", true) (or similar). This would update an existing Uri by adding things in it.

I've been searching the docs, but it does not seem like there is something like that in the library. Any pointers? (I wrote a helper function for now, but I find it annoyingly unsafe for something that I think should be safe)

lilac violet

My website code looks something like this below. The log middleware records a lot of messages for unmatched routes, people trying to hack my site hitting php urls, etc. I thought when I called layer(...) that the layer applied to everything before that point in the router. Does it also apply to the default fallback route? How would I make it so it did not log the fallback handler? Actually, I probably want to log it but to a different place.

 let app = Router::new()
    .route("/", get(homepage))
    .route("/some-other-page", ...)
    .nest("/app-apis", app_apis_router(pool.clone()))
    .layer(middleware::from_fn_with_state(pool.clone(), log_to_db_mw))
    .into_make_service_with_connect_info::<SocketAddr>();
dawn wedge

layer runs after routing, but it runs regardless of whether there was actually a match or not

route_layer only runs if there was a match

lilac violet

Weird I'm using the macro and still tells me to use it. Not sure what is wrong with my handler function here:

   [... router ...].fallback(my_fallback_handler)
#[axum::debug_handler]
async fn my_fallback_handler(State(pool): State<PgPool>, req: Request) -> Response { ...

Error:

error[E0277]: the trait bound `fn(State<Pool<...>>, ...) -> ... {my_fallback_handler}: Handler<_, ()>` is not satisfied
    --> web-site/src/lib.rs:153:19
     |
 153 |         .fallback(my_fallback_handler)
     |          -------- ^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, ()>` is not implemented for fn item `fn(State<Pool<Postgres>>, Request<Body>) -> ... {my_fallback_handler}`
     |          |
     |          required by a bound introduced by this call
     |
     = note: Consider using `#[axum::debug_handler]` to improve the error message
dawn wedge

there's nothing wrong with the handler, but with how you register it

you either forgot to call .with_state(…) or called it before .fallback(…)

lilac violet
let app = Router::new()
        .route("/", get(homepage))
        .route("/workout-timer", get(|| resource!("../resources/workout-timer.html")))            .nest("/app-apis", app_apis_router(pool.clone()))
        .route("/version", get(version))
        .nest_service("/styles", ServeDir::new(format!("{}/styles", static_dir)))
        .nest_service("/images", ServeDir::new(format!("{}/images", static_dir)))
        .route_layer(middleware::from_fn_with_state(pool.clone(), log_to_db_middleware))
        .fallback(my_fallback_handler)

I tried adding a with_state(pool.clone()), in the chain, but it's not compiling...

dawn wedge

with the same error or a different error

lilac violet
.route_layer(middleware::from_fn_with_state(pool.clone(), log_to_db_middleware))
.with_state(pool.clone())
.fallback(my_fallback_handler)

Error:

error[E0308]: mismatched types
   --> web-site/src/lib.rs:154:21
    |
154 |         .with_state(pool.clone())
    |          ---------- ^^^^^^^^^^^^ expected `()`, found `Pool<Postgres>`
    |          |
    |          arguments to this method are incorrect
    |
    = note: expected unit type `()`
                  found struct `Pool<Postgres>`

If put the with_state after fallback, it's the same error as before.

dawn wedge
lilac violet
error[E0277]: the trait bound `fn(State<Pool<...>>, ...) -> ... {my_fallback_handler}: Handler<_, ()>` is not satisfied
    --> web-site/src/lib.rs:154:19
     |
 154 |         .fallback(my_fallback_handler)
     |          -------- ^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, ()>` is not implemented for fn item `fn(State<Pool<Postgres>>, Request<Body>) -> ... {my_fallback_handler}`
     |          |
     |          required by a bound introduced by this call
     |
     = note: Consider using `#[axum::debug_handler]` to improve the error message
stuck steeple
pub async fn save_response(
    repo: &Repository,
    idempotency_key: &IdempotencyKey,
    user_id: &str,
    response: Response,
) -> anyhow::Result<()> {
    let status_code = response.status();

    let headers = {
        let mut h = Vec::with_capacity(response.headers().len());
        for (key, value) in response.headers().iter() {
            h.push(HeaderPair {
                key: key.as_str().to_owned(),
                value: value.as_bytes().to_owned(),
            });
        }
        h
    };

    repo.save_response(
        user_id,
        idempotency_key,
        status_code.as_u16(),
        headers,
        to_bytes(response.into_body(), usize::MAX).await?.to_vec(),
    )
    .await?;

    Ok(())
}

I want to refactor this function to return response back to caller. but the to_bytes function consumes response.

the repo.save_response expects Vec<u8> (bytes) as the last arg.


how do i get around it?

stuck steeple

i found a relevant method, into_parts that solved it.

swift junco

how could i make it os every route but one (/login) requires an token (eg via authentication header)?

spiral river
brave eagle
main escarp

Axum plus tower_http middleware question: (please let me know if there is a better place I should ask this)

When applying both CorsLayer and TraceLayer to a router, if I apply through ServiceBuilder, they must be applied in a particular order (Trace wrapping Cors) in order for it to compile.
However, if I use successive router.layer calls, the ordering does not matter. Does this mean the ordering doesn't actually matter, or do I need to apply them in the order enforced by the ServiceBuilder?

Example: the router.layer(ServiceBuilder...) call fails to compile with:

the trait bound `ResponseBody<Body, NeverClassifyEos<...>>: Default` is not satisfied
required for `Cors<Trace<Route, SharedClassifier<ServerErrorsAsFailures>>>` to implement `tower::Service<axum::http::Request<axum::body::Body>>`
use axum::{Router, routing::get};
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;

#[tokio::main]
async fn main() {
    let mut router = Router::new().route("/", get(|| async { "Hello, World!" }));

    router = router.layer( // this fails with the compilation error above
        ServiceBuilder::new()
            .layer(CorsLayer::new())
            .layer(TraceLayer::new_for_http()),
    );
    router = router // this is fine, despite applying the same middleware order
        .layer(TraceLayer::new_for_http())
        .layer(CorsLayer::new());

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, router).await.unwrap();
}

this is with axum 0.8.8, tower 0.5.3, tower-http 0.6.8

dawn wedge

you can use ServiceBuilder and retain the order if you map the body type to something that implements Default, or just use Router::layer()

main escarp

got it - thank you!

velvet warren

has anyone here looked into exposing fallible router merging? basically just exposing the errors that the private merge methods return and not converting it to a panic in Router::merge

it's not clear to me if it's just that simple or if there's some special reason why it hasn't already been exposed as a fallible try_merge method or something yet

candid shale

Is there anything to do when getting the "too many argument" warning but slapping an allow attribute to it?
I know that I could do something like this:

State(AppState {
    database,
    localization,
    config,
    ..
}): State<AppState>,

but it would clone the other AppState fields unnecessarily.

blazing nova
candid shale
blazing nova

then only Arcs will be cloned (unless you Arc the outer struct, then only that Arc will be cloned)

cloning Arc is very cheap since it is just a reference counter

candid shale

I'll pass the main state directly then, thanks. A few more arc clones is negligible

candid shale

@blazing nova I guess it's not possible to "merge" the extensions together, ie to have them all at once as a tuple?

They're not defined in the same middleware, so I cannot do it myself by hand.

dawn wedge

you could write your own extractor that does that

candid shale

Oh, I see. Thanks for your answer, it's something I'll consider.

cobalt horizon

Does anyone happen to have an example or sample project which implements mTLS with x509?
I've tried doing it myself, but i can never get it to work. I either run into lifetime issues or traits requirements not being met.

blazing nova
cobalt horizon

Perhaps you are right, i wasn't sure where to ask since it seems to involve, axum, tower and hyper at the same time.

blazing nova

tbh it does not really involve hyper either

it's a rustls thing, but hyper does deal with it

cobalt horizon

I see, thanks for the clarification. My knowledge about all of this is very fleeting at best.

tawny violet
orchid plume

Hey! I built and open-source api-error at work, it is a proc macro to define HTTP errors in a thiserror way and it can automatically implement IntoResponse for it and can be used like this.

#[derive(Debug, thiserror::Error, ApiError)]
enum AppError {
    // Unnamed fields with positional formatting
    #[error("Database error: {0}")]
    #[api_error(status_code = 500, message = "Database operation failed: {0}")]
    Database(String),

    // Named fields with named formatting
    #[error("Validation failed on {field}")]
    #[api_error(status_code = 422, message = "Field `{field}` has invalid value")]
    Validation { field: String, value: String },
}

Currently the way the error is serialized through the IntoResponse impl is quite opinionated, there is only a JSON message field and the status is set on the headers. However I guess a everyone will have it's own kind of implementation. Therefore I'm quite curious about how you usually serialize your HTTP errors? Based on this we might think on a way to provide custom IntoResponse impl through a static maybe.

candid shale

I ignore if it's well-known: if a state is built for the whole webserver lifetime and won't change, you can leak it. I've used it without issue, and I love it:

#[derive(Debug, Clone)]
pub struct AppState {
    pub database: Database,
    pub cache: Cache,
    pub localization: Localization,
    pub config: &'static AppConfig,
    pub webauthn: &'static Webauthn,
}

#[derive(Clone, Copy)]
pub struct Localization {
    pub loader: &'static ArcLoader,
    language_options: &'static [(Language, &'static [LanguageOption])],
}

It's the easiest for memory management.

sage oasis

Hey, I submitted a new project in the economic system, but there has been no review

tender storm

What happened to axum (server) struct/enum?

dawn wedge
tender storm
manic fern

how to generate opaque tokens with axum?

like what cryptographic method should you use ideally

blazing nova

axum does not handle cryptography, it is up to you
you might find useful information on OWASP and elsewhere

frank ingot

Hi all I'm new here. I'm evaluating to contribute to axum, so right now just studing the code. But, just a questione, how do you track the v0.9 issues and roadmap? Is there a specific page or label or something else?

dawn wedge

or an issue/discussion on github, of course

blazing nova

<@&1207366540299735151> ^ :(

in other channels too

velvet warren

ban 🔨

west oracle
velvet warren

dang, i set it to do previous 24 hours

weird

west oracle

might just be a discord moment

velvet warren

yeah, i started deleting manually but it seems to have finally kicked in

blazing nova

yay

runic hearth

hello, i run into a strange implementation:

how do i use either::Either as a Path extractor, i want to extract something like this:

pub async fn get_food(Path(usernameOrId): Path<Either<String, Uuid>>) -> Result<impl IntoResponse, Error>

how?

south sage
runic hearth hello, i run into a strange implementation: how do i use `either::Either` as a ...

I'd rather use something like this:

#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum UuidOrString {
    Uuid(uuid::Uuid), // Has to be before String one, otherwise it would always deserialize to the string variant
    Str(String),
}

pub async fn get_food(
    Path(param): Path<UuidOrString>,
) -> Result<impl IntoResponse, Error> {
    println!("param: {param:?}");
    Ok(StatusCode::NO_CONTENT)
}

If based on your param name usernameOrId, the meanings are different, then this of course completely breaks down as soon as as you encounter someone making a username that deserializes into a UUID and you really should have separate routes instead.