Skip to content

Conversation

yawn
Copy link

@yawn yawn commented Mar 25, 2025

This demonstrates using associated methods as handlers or middleware, hopefully in a sensible fashion.

Motivation

Axum is quite powerful but (especially to beginners) it's not very straightforward to understand in terms of API. This example demonstrates a potential solution on using associated methods as an alternative / next to state.

Solution

Just an example with a simple macro. This could be changed / further expanded e.g.

  1. Show a solution with and without macro usage
  2. Show the difference between usage in handlers and middleware

Let me know if that makes sense (at all) and if I should expand on that.

This demonstrates using associated methods as handlers or middleware.
@mladedav
Copy link
Collaborator

It's cool that we can do this, but personally, I don't think we should guide people towards this.

I get that some people are confused about State but I don't think that a custom macro or capturing of some form of different state is a solution since people will just be confused about what's the preferred way to handle state. Even now people are asking whether to put state in Arc just because some examples in docs do it while other don't.

@yawn
Copy link
Author

yawn commented Mar 25, 2025

Macro: makes sense, I just added it because why not. Essentially it's mostly about demonstrating how to use associated methods for handlers and middleware (with the fn_mut caveat). That could even be a documentation PR that essentially documents

.route(
    "/aa",
    get({
        let s = Arc::clone(&s);
        |req| async { s.a(req).await }
    }),
)

and

.layer(from_fn({
    let s = Arc::clone(&s);
    move |req, next| {
        let s = Arc::clone(&s);
        async move { s.c(req, next).await }
    }
}))

I get what you're saying about users being confused about the relationship between Arc and State but I also feel that State is not always the most appropriate place, especially in larger architectures. Even with partial state extraction the state is still there - when building e.g. an auth layer I usually like the config of that middleware being available only to that particular struct.

Not sure how to proceed. What do others think?

@yawn
Copy link
Author

yawn commented Mar 26, 2025

FWIW: I seemed to have missed that it is somewhat documented (but somewhat confusingly maybe for new users in the context of accessing State) for handlers.

@tcanabrava
Copy link
Contributor

the code misses the point, in my opinion, as it's not commented on the why some choice is made.
this code is full of foos and bars, but by not being real code, comments are more than needed, specially for newcomers.

@yawn
Copy link
Author

yawn commented Mar 26, 2025

the code misses the point, in my opinion, as it's not commented on the why some choice is made. this code is full of foos and bars, but by not being real code, comments are more than needed, specially for newcomers.

Fair. Does it make sense generally in your opinion to document this pattern? If yes, as code using a more concrete example (not just Foo & Bar - I'll probably use authentication as an example) or rather as documentation?

@tcanabrava
Copy link
Contributor

the code misses the point, in my opinion, as it's not commented on the why some choice is made. this code is full of foos and bars, but by not being real code, comments are more than needed, specially for newcomers.

Fair. Does it make sense generally in your opinion to document this pattern? If yes, as code using a more concrete example (not just Foo & Bar - I'll probably use authentication as an example) or rather as documentation?

I don't mind the foos and bars, but without comments, it's hard to follow. so I'd say "comment it good, explaining the choices".

The example is now concrete, without abstract identifiers and a without a macro.
@yawn
Copy link
Author

yawn commented Mar 27, 2025

Made the example more simple and more concrete. Does that make sense?

Furthermore: does it make sense to include a macro in axum that makes such a pattern more ergonomic or is there a general sense (like @mladedav indicated) that this is an anti-pattern (I'd argue not)?

Copy link
Member

@jplatte jplatte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yanns @Turbo87 any thoughts on this?

//! can be useful to create routes and middleware directly on specific structs (as opposed to
//! handling everything in a central application state).
//!
//! This example demonstrates how do that using closures.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! This example demonstrates how do that using closures.
//! This example demonstrates how do that using closures.

Copy link
Collaborator

@yanns yanns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's an interesting approach

@Turbo87
Copy link
Collaborator

Turbo87 commented Jul 10, 2025

I tend to agree with @mladedav. Having too many ways to achieve the same thing tends to cause confusion at some point. If we were to add this we should at least explicitly document in which situation this is appropriate compared to the recommended approach of using router state.

@yawn
Copy link
Author

yawn commented Jul 10, 2025

Not entirely sure how the consensus process here works - happy with the PR being merged but I can also rework it into documentation. Let me know (or merge)!

Regarding the remark from above @Turbo87 - we approach state management roughly like this:

  • Application state that is relevant for multiple middleware or handlers goes into the axum state
  • Middleware state (that is really just and only relevant for one specific middleware) is managed using the pattern from above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants