Backend Async Patterns (Rust) banner
solanabr solanabr

Backend Async Patterns (Rust)

Development community intermediate

Description

Production-ready async patterns using Axum 0.8+, Tokio, and Solana clients for building backend services, indexers, and APIs.

Installation

This entry records only its repository, not the path inside it, so there is no exact command to give. Open the source below and copy the folder into ~/.claude/skills/, or the file into ~/.claude/agents/.

Repository README

This is the README for solanabr/solana-claude, shared by 5 entries in this directory. It describes the repository, not this entry specifically.

Backend Async Patterns (Rust)

Production-ready async patterns using Axum 0.8+, Tokio, and Solana clients for building backend services, indexers, and APIs.

Modern Stack (2026)

  • Axum 0.8+: Web framework (no more #[async_trait] needed!)
  • Tokio 1.40+: Async runtime
  • Tower: Middleware (compression, tracing, timeouts)
  • sqlx: Async database with compile-time checked queries
  • solana-client: Async Solana RPC client
  • Redis: Caching layer for RPC responses

Axum 0.8 Server Setup

Basic Server Pattern

use axum::{
    Router,
    routing::{get, post},
    extract::{State, Path},
    response::IntoResponse,
    http::StatusCode,
    Json,
};
use tokio::net::TcpListener;
use tower_http::{
    trace::TraceLayer,
    compression::CompressionLayer,
};

#[derive(Clone)]
struct AppState {
    db: sqlx::PgPool,
    solana_client: Arc,
    redis: redis::Client,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Tracing setup
    tracing_subscriber::fmt::init();

    // Database pool
    let db = sqlx::postgres::PgPoolOptions::new()
        .max_connections(50)
        .connect(&env::var("DATABASE_URL")?)
        .await?;

    // Solana client
    let solana_client = Arc::new(RpcClient::new_with_commitment(
        env::var("SOLANA_RPC_URL")?,
        CommitmentConfig::confirmed(),
    ));

    // Redis for caching
    let redis = redis::Client::open(env::var("REDIS_URL")?)?;

    let state = AppState { db, solana_client, redis };

    // Axum 0.8: New path syntax with {}
    let app = Router::new()
        .route("/health", get(health_check))
        .route("/api/accounts/{pubkey}", get(get_account))
        .route("/api/transactions", post(submit_transaction))
        .route("/api/program/{program_id}/accounts", get(get_program_accounts))
        .layer(TraceLayer::new_for_http())
        .layer(CompressionLayer::new())
        .with_state(state);

    let listener = TcpListener::bind("0.0.0.0:3000").await?;
    tracing::info!("Server listening on {}", listener.local_addr()?);

    axum::serve(listener, app).await?;
    Ok(())
}

Handler Patterns (No #[async_trait] Needed!)

Modern Handler (Rust 1.75+)

use axum::extract::{State, Path, Json};
use serde::{Deserialize, Serialize};

// ✅ MODERN - No #[async_trait] macro needed!
async fn get_user(
    State(state): State,
    Path(user_id): Path,
) -> Result, AppError> {
    let user = sqlx::query_as!(
        User,
        "SELECT * FROM users WHERE id = $1",
        user_id
    )
    .fetch_one(&state.db)
    .await?;

    Ok(Json(user))
}

async fn create_user(
    State(state): State,
    Json(payload): Json,
) -> Result<(StatusCode, Json), AppError> {
    let user = sqlx::query_as!(
        User,
        r#"
        INSERT INTO users (name, wallet_address)
        VALUES ($1, $2)
        RETURNING *
        "#,
        payload.name,
        payload.wallet_a