Skip to content

Comments

refactor: Decouple extractors from AppContext using FromRef#1735

Open
mccormickt wants to merge 1 commit intoloco-rs:masterfrom
Adversarial-Risk-Management:appcontext-fromref
Open

refactor: Decouple extractors from AppContext using FromRef#1735
mccormickt wants to merge 1 commit intoloco-rs:masterfrom
Adversarial-Risk-Management:appcontext-fromref

Conversation

@mccormickt
Copy link
Contributor

@mccormickt mccormickt commented Jan 26, 2026

This PR derives FromRef::from_ref on AppContext to allow extractors to pull out fields of AppContext as substates in handers. It also refactors our existing extractors to be able to work with any state that implements FromRef to pull out the required substate (e.g. DatabaseConnection, Config, SharedStore, Cache, etc.).

@mccormickt mccormickt changed the title chore: Derive FromRef on AppContext refactor: Decouple extractors from AppContext using FromRef Jan 26, 2026
@kaplanelad
Copy link
Contributor

kaplanelad commented Feb 19, 2026

Thanks for the clean work here! A few questions before we move forward:

  1. What's the concrete use case? Are you building something where you need these extractors to work with a custom state type
    instead of AppContext? We'd love to understand the motivation, Loco intentionally couples to AppContext as the single state
    convention.
  2. get_jwt_from_config is a public API break — the signature changed from &AppContext to &Config. Even if nobody in our repo
    calls it externally, downstream users might.

@mccormickt
Copy link
Contributor Author

Thanks for

  1. What's the concrete use case? Are you building something where you need these extractors to work with a custom state type instead of AppContext? We'd love to understand the motivation — Loco intentionally couples to AppContext as the single state convention.

There are a few nice properties of this change that I believe still uphold this motivation:

By deriving FromRef, middleware implemented external to Loco that relies only state using a subset of the fields of AppContext would be able work with/without AppContext. SeaORM's DatabaseConnection probably being the most common example. This is the main piece that would allow bridging Loco and external crates and is transparent to existing users.

By modifying our extractors, we allow external crates to utilize helpful middleware we may implement, by not explicitly relying on AppContext. I don't believe we should be restrictive where we don't need to be.

  1. get_jwt_from_config is a public API break — the signature changed from &AppContext to &Config. Even if nobody in our repo
    calls it externally, downstream users might.

I agree this is not a technically necessary change, but seemed more "correct" in that the JWT methods there don't require anything but data in Config, as the function name implies. Given the pre-1.0 state, precedent of other small breaking changes (#1418), and the ease of migration for this, I considered it was worth it.

Those are my strong opinions, loosely held! The main gains are in the FromRef derivation, but I think overall this makes the codebase more extensible. WDYT?

@kaplanelad
Copy link
Contributor

I agree on the core value here, deriving FromRef on AppContext is a clean win. It's purely additive (just adds new trait impls for each field), doesn't change any existing behavior, and opens
up the interop story you described with external middleware. Let's definitely keep that.

However, I'd like to propose we split this into two steps:

Step 1 (this PR): Just derive FromRef on AppContext + the from_ref tests. This alone gives external crate authors what they need — they can write middleware with where DatabaseConnection: FromRef<S> and it'll work transparently with AppContext. Zero breaking changes, immediate value.

Step 2 (follow-up PR): Loosen our built-in extractor bounds + the get_jwt_from_config signature change. I want to think through the maintenance implications more carefully:

  • When extractors use where AppContext: FromRef<S>, we can freely change what fields they access internally — that's an implementation detail. If we switch to where Config: FromRef<S>, DatabaseConnection: FromRef<S>, those bounds become a public API contract. Adding a new field access to an extractor (say, if JWTWithUser later needs cache) becomes a breaking change for anyone
    using a custom state type. That's a maintenance tradeoff worth being deliberate about.

  • On the #1418 precedent — that PR actually went the opposite direction (widening &Config&AppContext to give access to SharedStore). This PR narrows &AppContext&Config. Both can be
    valid changes, but they have different motivations, so I wouldn't call it direct precedent.

  • On get_jwt_from_config — you make a fair point that the function name implies &Config is the more correct parameter. It was explicitly made public in make extract_token and get_jwt_from_config fn public #1495 though, so we should document the
    migration in the upgrade guide if we go that route. The fix is trivial (&ctx&ctx.config), but we owe downstream users clear documentation.

Could you restructure this PR to just the FromRef derivation + tests? (step One(
and on step 2 we can still discuss here

Thoughts?

This allows extractors to pull out fields of `AppContext` as [substates](https://docs.rs/axum/0.8.8/axum/extract/struct.State.html\#substates) in handlers through [`FromRef::from_ref`](https://docs.rs/axum/latest/axum/extract/trait.FromRef.html)
@mccormickt
Copy link
Contributor Author

Done - I've pulled out the commit changing get_jwt_from_config and the existing extractor trait bounds. I'll follow up with an issue or another PR where we can hash out the implications of that 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants