# How to Use App Context for Sharing Values Across Requests in Topcoat

> Learn how to share values across requests in Topcoat using app context. Access global state like DB pools and configs safely in your handlers.

- Repository: [Tokio/topcoat](https://github.com/tokio-rs/topcoat)
- Tags: how-to-guide
- Published: 2026-07-21

---

**In Topcoat, you can share global state across all requests by registering values with `Router::builder().app_context(value)` and retrieving them in handlers using `app_context::<T>(cx)`, which provides thread-safe, type-keyed access to resources like database pools and configuration structs.**

Topcoat provides a powerful context system for managing state in asynchronous web applications. Learning how to use app context for sharing values across requests in Topcoat allows you to efficiently share resources like connection pools and caches without cloning data per request. This guide covers the implementation details, registration methods, and retrieval patterns based on the current tokio-rs/topcoat source code.

## Understanding App Context vs Request Context

Topcoat distinguishes between two scopes of state storage within the request context (`Cx`):

- **App context**: Global to the router and shared by every request. It lives for the entire lifetime of the `Router` and is not cloned per request. Handlers receive a reference (`&T`), making it ideal for database pools, configuration structs, or caches.

- **Request context**: Specific to a single request and dropped when the request ends. This is used for request-specific data like parsed form inputs or authentication results.

Both contexts use a **type-keyed storage** system backed by `ContextMap` in [`crates/topcoat-core/src/context/context_map.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-core/src/context/context_map.rs). This guarantees that each concrete type (`T`) can only be registered once, preventing accidental overwrites and providing compile-time safety through generics bounded by `Any + Send + Sync`.

## Registering App Context Values

You register app context values while building the router using the builder pattern. The `Router::app_context` method (defined in [`crates/topcoat-router/src/router.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-router/src/router.rs)) accepts any type that implements `Send + Sync` and stores it in the global context map.

```rust
use topcoat::{context::{Cx, app_context}, Router};
use std::sync::atomic::{AtomicU64, Ordering};

#[derive(Debug)]
struct PageViews(AtomicU64);

fn main() {
    let router = Router::builder()
        .discover()                     // auto-register routes
        .app_context(PageViews(AtomicU64::new(0)))
        .app_context(DbPool::new())     // register a DB pool for all requests
        .build();

    topcoat::start(router);
}

```

The value is stored directly in the router and shared across all requests as a reference, not a clone. This is critical for performance when sharing large structures like database connection pools across thousands of concurrent connections.

## Retrieving Shared State in Handlers

Handlers retrieve app context values using the `app_context` function, which performs a type-keyed lookup in the `ContextMap`. The function panics if the requested type was not registered, helping catch configuration errors early during development.

```rust
use topcoat::context::{Cx, app_context};

async fn home(cx: &Cx) -> impl topcoat::response::Response {
    // Increment the shared page-view counter
    let views = app_context::<PageViews>(cx);
    let count = views.0.fetch_add(1, Ordering::Relaxed) + 1;

    format!("This page has been viewed {count} times")
}

```

For optional configuration values where the type might not always be registered, use `try_app_context`, which returns `Option<&T>` instead of panicking:

```rust
use topcoat::context::{Cx, try_app_context};

async fn maybe_feature(cx: &Cx) -> impl topcoat::response::Response {
    if let Some(cfg) = try_app_context::<FeatureConfig>(cx) {
        format!("Feature enabled: {}", cfg.description)
    } else {
        "Feature not configured".into()
    }
}

```

The implementation of these retrieval functions lives in [`crates/topcoat-core/src/context/context_map.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-core/src/context/context_map.rs) (lines 35-41 for `try_app_context` and lines 68-79 for `app_context`).

## Cross-Crate Usage Patterns

App context enables middleware and helper crates to access shared resources registered by the main application. For example, the cookie crate accesses a signing key stored in app context:

```rust
use topcoat_core::context::{Cx, app_context};
use topcoat_cookie::Key;

fn sign_cookie(cx: &Cx, data: &str) -> String {
    // `Key` was registered via `router.app_context(Key::generate())`
    let key = app_context::<Key>(cx);
    cookies(cx).signed(key).sign(data)
}

```

This pattern appears in [`crates/topcoat-cookie/src/lib.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-cookie/src/lib.rs) (lines 283-287), demonstrating how app context facilitates loose coupling between components while maintaining type safety.

## Summary

- **Type-keyed storage**: Each type can be registered exactly once in the app context, enforced by the `ContextMap` implementation in [`crates/topcoat-core/src/context/context_map.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-core/src/context/context_map.rs).
- **Global lifetime**: App context values live for the entire `Router` lifetime and are shared across all requests as references (`&T`), avoiding per-request cloning overhead.
- **Registration**: Use `Router::builder().app_context(value)` during application startup to inject shared resources like database pools or configuration structs.
- **Retrieval**: Use `app_context::<T>(cx)` for required values (panics if missing) or `try_app_context::<T>(cx)` for optional values (returns `Option<&T>`).
- **Thread safety**: All app context values must implement `Send + Sync`, ensuring safe concurrent access across Tokio's async runtime.

## Frequently Asked Questions

### What happens if I call `app_context` for a type that wasn't registered?

The application will panic at runtime. According to the implementation in [`crates/topcoat-core/src/context/context_map.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-core/src/context/context_map.rs), the `app_context` function expects the type to exist and panics if the lookup fails. This design choice helps catch configuration errors immediately during development rather than silently ignoring missing dependencies. Use `try_app_context` if you need to handle potentially missing values gracefully.

### Is app context thread-safe for concurrent access?

Yes. The `ContextMap` requires all stored types to implement `Send + Sync`, and the storage itself uses thread-safe internal mechanisms. Since handlers receive immutable references (`&T`) to the shared data, you should use interior mutability (like `AtomicU64` or `RwLock`) if you need to modify shared state, as shown in the `PageViews` example from [`examples/app-context/src/main.rs`](https://github.com/tokio-rs/topcoat/blob/main/examples/app-context/src/main.rs).

### Can I register multiple instances of the same type in app context?

No. The type-keyed storage system prevents this by design. Since the `ContextMap` uses the type itself as the lookup key, attempting to register the same type twice would overwrite the previous value. If you need multiple instances of similar data, wrap them in distinct newtype structs or use a collection type like `HashMap<String, T>` as your context value.

### How is app context different from request context?

App context is global to the router and shared across all requests, while request context is specific to individual requests. You access app context using `app_context(cx)` for data like database pools, and request context using `request_context(cx)` for data like parsed request bodies or authentication tokens that only apply to the current request.