Skip to main content

Crate rust_rfc7807

Crate rust_rfc7807 

Source
Expand description

§rust-rfc7807

RFC 7807 Problem Details for HTTP APIs.

This crate provides a lightweight, framework-agnostic Problem type that serializes to application/problem+json with safe defaults, an ergonomic builder API, and first-class support for validation errors, error codes, and trace correlation.

Core dependencies: serde and serde_json only.

§Creating a Problem

use rust_rfc7807::Problem;

let problem = Problem::not_found()
    .type_("https://api.example.com/problems/user-not-found")
    .title("User not found")
    .detail("No user with ID 42 exists")
    .instance("/users/42")
    .code("USER_NOT_FOUND")
    .trace_id("abc-123");

let json = serde_json::to_value(&problem).unwrap();
assert_eq!(json["status"], 404);
assert_eq!(json["type"], "https://api.example.com/problems/user-not-found");
assert_eq!(json["code"], "USER_NOT_FOUND");
assert_eq!(json["trace_id"], "abc-123");

§Validation Errors

Problem::validation() returns a 422 with type set to "validation_error". Use Problem::push_error and Problem::push_error_code to add field-level errors:

use rust_rfc7807::Problem;

let problem = Problem::validation()
    .push_error_code("email", "must be a valid email address", "INVALID_EMAIL")
    .push_error("name", "is required")
    .code("VALIDATION_ERROR");

let json = serde_json::to_value(&problem).unwrap();
assert_eq!(json["status"], 422);
assert_eq!(json["type"], "validation_error");

let errors = json["errors"].as_array().unwrap();
assert_eq!(errors[0]["field"], "email");
assert_eq!(errors[0]["code"], "INVALID_EMAIL");
assert_eq!(errors[1]["field"], "name");

§Security: Internal Causes Never Serialize

For 5xx errors, Problem defaults to a generic public message. Use Problem::with_cause to attach a diagnostic error for server-side logging that is never included in JSON output:

use rust_rfc7807::Problem;

let problem = Problem::internal_server_error()
    .with_cause(std::io::Error::other("connection to db:5432 refused"));

// Safe for clients — no internal details
let json = serde_json::to_string(&problem).unwrap();
assert!(!json.contains("db:5432"));
assert!(json.contains("An unexpected error occurred."));

// Available for server-side logging
let cause = problem.internal_cause().unwrap();
assert!(cause.to_string().contains("db:5432"));

§Mapping Domain Errors

Implement IntoProblem on your application’s error types:

use rust_rfc7807::{IntoProblem, Problem};

enum AppError {
    UserNotFound(u64),
}

impl IntoProblem for AppError {
    fn into_problem(self) -> Problem {
        match self {
            AppError::UserNotFound(id) => Problem::not_found()
                .detail(format!("No user with ID {id}"))
                .code("USER_NOT_FOUND"),
        }
    }
}

let problem = AppError::UserNotFound(42).into_problem();
assert_eq!(problem.status, Some(404));
assert_eq!(problem.get_code(), Some("USER_NOT_FOUND"));

§Extension Fields

Arbitrary extension fields are flattened into the top-level JSON object:

use rust_rfc7807::Problem;

let problem = Problem::new(429)
    .code("RATE_LIMITED")
    .extension("retry_after", 30);

let json = serde_json::to_value(&problem).unwrap();
assert_eq!(json["retry_after"], 30);
assert_eq!(json["code"], "RATE_LIMITED");

§Axum Integration

Enable the axum feature for IntoResponse on Problem, or use the companion crate rust-rfc7807-axum for the full integration including ApiError.

Structs§

Problem
An RFC 7807 Problem Details object.
ValidationItem
A single field-level validation error.

Constants§

APPLICATION_PROBLEM_JSON
The Content-Type header value for RFC 7807 problem responses.

Traits§

IntoProblem
Convert a value into an RFC 7807 Problem.