lmrc-http-common 0.3.16

Common HTTP utilities and patterns for LMRC Stack applications
Documentation
# lmrc-http-common

Common HTTP utilities and patterns for LMRC Stack applications.

This library provides reusable components for building Axum-based HTTP services with best practices built-in.

## Features

- **Error Handling**: Standard HTTP error types with automatic response conversion
- **Response Wrappers**: Success, created, empty, and paginated response types
- **Middleware**: CORS, logging, request ID, and more
- **Authentication** (optional): JWT, password hashing, session management
- **Configuration** Management: Server and database configuration loading
- **Health Checks**: Standardized health check framework
- **Validated Extractors** (optional): Auto-validate requests with `validator` crate
- **Server Bootstrap** (optional): Quick server setup with tracing and configuration

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
lmrc-http-common = "0.3.11"
```

### Feature Flags

- `auth` (default) - Authentication utilities (JWT, bcrypt, sessions)
- `validation` (default) - Request validation helpers
- `server` - Server bootstrap utilities (requires `tracing-subscriber`, `dotenvy`)

To enable all features:

```toml
[dependencies]
lmrc-http-common = { version = "0.3.11", features = ["server"] }
```

## Quick Start

### Basic Handler with Error Handling

```rust
use lmrc_http_common::{
    error::{HttpError, HttpResult},
    response::SuccessResponse,
};
use axum::{Router, routing::get, Json};

async fn get_user(id: u32) -> HttpResult<Json<SuccessResponse<User>>> {
    let user = database
        .find_user(id)
        .await
        .ok_or_else(|| HttpError::NotFound(format!("User {} not found", id)))?;

    Ok(Json(SuccessResponse::new(user)))
}
```

### Configuration Loading

```rust
use lmrc_http_common::config::{ServerConfig, DatabaseConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Load configuration from environment variables
    let server_config = ServerConfig::from_env()?;
    let db_config = DatabaseConfig::from_env(None)?;

    println!("Starting server on {}", server_config.bind_addr()?);
    Ok(())
}
```

### Health Checks

```rust
use lmrc_http_common::health::{HealthChecker, HealthCheck, CheckResult};
use std::sync::Arc;
use async_trait::async_trait;

struct DatabaseHealthCheck {
    db: DatabaseConnection,
}

#[async_trait]
impl HealthCheck for DatabaseHealthCheck {
    async fn check(&self) -> CheckResult {
        match self.db.ping().await {
            Ok(_) => CheckResult::healthy_with_message("Database connected"),
            Err(e) => CheckResult::unhealthy(format!("Database error: {}", e)),
        }
    }

    fn name(&self) -> &str {
        "database"
    }
}

// In your router setup:
let checker = Arc::new(
    HealthChecker::new(env!("CARGO_PKG_VERSION"))
        .add_check(Arc::new(DatabaseHealthCheck { db }))
);

let app = Router::new()
    .route("/health", get(lmrc_http_common::health::health_handler))
    .with_state(checker);
```

### Validated Request Extractors

```rust
use lmrc_http_common::extractors::ValidatedJson;
use serde::Deserialize;
use validator::Validate;

#[derive(Deserialize, Validate)]
struct CreateUser {
    #[validate(length(min = 3, max = 50))]
    username: String,
    #[validate(email)]
    email: String,
}

async fn create_user(
    ValidatedJson(payload): ValidatedJson<CreateUser>
) -> &'static str {
    // payload is automatically validated!
    // Returns 422 Unprocessable Entity if validation fails
    "User created"
}
```

### Server Bootstrap (with `server` feature)

```rust
use axum::{Router, routing::get};
use lmrc_http_common::server::ServerBootstrap;

#[derive(Clone)]
struct AppState {
    config: Config,
    db: DatabaseConnection,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let state = AppState {
        config: Config::from_env()?,
        db: DatabaseConnection::new().await?,
    };

    ServerBootstrap::with_state(state)
        .with_tracing("myapp")
        .with_port(8080)
        .with_router(|state| {
            Router::new()
                .route("/health", get(|| async { "OK" }))
                .with_state(state)
        })
        .serve()
        .await
}
```

Or use the quick start helper:

```rust
use axum::{Router, routing::get};
use lmrc_http_common::server::quick_start;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let router = Router::new().route("/health", get(|| async { "OK" }));
    quick_start("myapp", router, 8080).await
}
```

## Module Overview

### `error`

Standard HTTP error types that automatically convert to appropriate HTTP responses:

- `HttpError::BadRequest(String)` → 400
- `HttpError::Unauthorized(String)` → 401
- `HttpError::Forbidden(String)` → 403
- `HttpError::NotFound(String)` → 404
- `HttpError::ValidationError(String)` → 422
- `HttpError::InternalServer(String)` → 500

Use the `app_error!` macro to create custom error types:

```rust
use lmrc_http_common::app_error;

app_error! {
    MyAppError {
        BusinessLogic(String),
        ExternalApi(String),
    }
}
```

### `response`

Standard response wrappers:

- `SuccessResponse<T>` - Standard success response with data
- `CreatedResponse<T>` - 201 Created with location header
- `EmptyResponse` - 204 No Content
- `PaginatedResponse<T>` - Paginated list with metadata

### `middleware`

Reusable middleware layers:

- `add_request_id` - Adds unique request ID to each request
- `log_request` - Logs all requests with duration
- `cors_with_origins(Vec<String>)` - CORS with specific origins

### `config`

Configuration management:

- `ServerConfig` - Host, port, CORS origins
- `DatabaseConfig` - Database URL, connection pool settings
- `ConfigLoader` trait - For custom configuration types

### `health`

Health check framework:

- `HealthStatus` - Overall application health
- `HealthCheck` trait - Implement for custom checks
- `HealthChecker` - Aggregates multiple health checks
- `health_handler` - Axum handler for health endpoint

### `auth` (requires `auth` feature)

Authentication utilities:

- `jwt` - JWT token creation and verification
- `password` - Password hashing and verification (bcrypt)
- `session` - Session management

### `extractors` (requires `validation` feature)

Auto-validating request extractors:

- `ValidatedJson<T>` - JSON with automatic validation
- `ValidatedQuery<T>` - Query parameters with validation

### `server` (requires `server` feature)

Server bootstrap utilities:

- `ServerBootstrap` - Fluent API for server setup
- `quick_start` - Simple one-liner server start

## Examples

See the [examples directory](../../apps/lmrc-cli/embedded-apps/) for complete working examples:

- `gateway` - API gateway with routing and auth
- `api-service-template` - Standard REST API template
- `infra-api` - Infrastructure management API

## Development

### Running Tests

```bash
cargo test -p lmrc-http-common --all-features
```

### Building Documentation

```bash
cargo doc --open -p lmrc-http-common --all-features
```

## License

Dual licensed under MIT OR Apache-2.0 (your choice).

## Contributing

This library is part of the [LMRC Stack](https://gitlab.com/lemarco/lmrc-stack) monorepo.

See the main repository for contribution guidelines.