No release in over 3 years
hati-jsonapi-error is a Ruby gem for Standardized JSON Error.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies
 Project Readme

Hati JSON:API Error

Gem Version Ruby License: MIT Test Coverage

Production-ready JSON:API-compliant error responses for professional Web APIs

Transform inconsistent error handling into standardized, traceable responses. Built for Ruby applications requiring enterprise-grade error management.

Table of Contents

  • Why Standardized Error Handling Matters
    • The Problem: Inconsistent Error Responses
    • The Impact
    • The Solution: JSON:API Standard
  • ✨ Features
  • Installation
  • Quick Start
    • 1. Configuration
    • 2. Basic Usage
  • Usage Examples
    • Basic Error Handling
    • Rich Error Context
    • Multiple Validation Errors
  • Controller Integration
    • Custom Error Classes
  • Functional Programming Integration
  • Configuration
    • Error Mapping
  • Available Error Classes
  • Testing
    • RSpec Integration
    • Unit Testing
  • Benefits
  • Contributing
  • License

Why Standardized Error Handling Matters

The Problem: Inconsistent Error Responses

Different controllers returning different error formats creates maintenance nightmares:

# Three different error formats in one application
class UsersController
  def show
    render json: { error: "User not found" }, status: 404
  end
end

class OrdersController
  def create
    render json: { message: "Validation failed", details: errors }, status: 422
  end
end

class PaymentsController
  def process
    render json: { errors: errors, error_code: "INVALID", status: "failure" }, status: 400
  end
end

This forces frontend developers to handle multiple error formats:

// Unmaintainable error handling
if (data.error) {
  showError(data.error); // Users format
} else if (data.message && data.details) {
  showError(`${data.message}: ${data.details.join(", ")}`); // Orders format
} else if (data.errors && data.error_code) {
  showError(`${data.error_code}: ${data.errors.join(", ")}`); // Payments format
}

The Impact

  • API Documentation: Each endpoint needs custom error documentation
  • Error Tracking: Different structures break centralized logging
  • Client SDKs: Cannot provide consistent error handling
  • Testing: Each format requires separate test cases
  • Team Coordination: New developers must learn multiple patterns

The Solution: JSON:API Standard

One format across all endpoints:

raise HatiJsonapiError::UnprocessableEntity.new(
  detail: "Email address is required",
  source: { pointer: "/data/attributes/email" }
)

Always produces standardized output:

{
  "errors": [
    {
      "status": 422,
      "code": "unprocessable_entity",
      "title": "Validation Failed",
      "detail": "Email address is required",
      "source": { "pointer": "/data/attributes/email" }
    }
  ]
}

✨ Features

  • JSON:API Compliant - Follows the official JSON:API error specification
  • Auto-Generated Error Classes - Dynamic HTTP status code error classes (400-511)
  • Rich Error Context - Support for id, code, title, detail, status, meta, links, source
  • Error Registry - Map custom exceptions to standardized responses
  • Controller Integration - Helper methods for Rails, Sinatra, and other frameworks
  • 100% Test Coverage - Comprehensive RSpec test suite
  • Zero Dependencies - Lightweight and fast
  • Production Ready - Thread-safe and memory efficient

Installation

# Gemfile
gem 'hati-jsonapi-error'
bundle install

Quick Start

1. Configuration

# config/initializers/hati_jsonapi_error.rb
HatiJsonapiError::Config.configure do |config|
  config.load_errors!

  config.map_errors = {
    ActiveRecord::RecordNotFound => :not_found,
    ActiveRecord::RecordInvalid   => :unprocessable_entity,
    ArgumentError                 => :bad_request
  }

  config.use_unexpected = HatiJsonapiError::InternalServerError
end

2. Basic Usage

# Simple error raising
raise HatiJsonapiError::NotFound.new
raise HatiJsonapiError::BadRequest.new
raise HatiJsonapiError::Unauthorized.new

Usage Examples

Basic Error Handling

Access errors multiple ways:

# By class name
raise HatiJsonapiError::NotFound.new

# By status code
api_err = HatiJsonapiError::Helpers::ApiErr
raise api_err[404]

# By error code
raise api_err[:not_found]

Rich Error Context

Add debugging information:

HatiJsonapiError::NotFound.new(
  id: 'user_lookup_failed',
  detail: 'User with email john@example.com was not found',
  source: { pointer: '/data/attributes/email' },
  meta: {
    searched_email: 'john@example.com',
    suggestion: 'Verify the email address is correct'
  }
)

Multiple Validation Errors

Collect and return multiple errors:

errors = []
errors << HatiJsonapiError::UnprocessableEntity.new(
  detail: "Email format is invalid",
  source: { pointer: '/data/attributes/email' }
)
errors << HatiJsonapiError::UnprocessableEntity.new(
  detail: "Password too short",
  source: { pointer: '/data/attributes/password' }
)

resolver = HatiJsonapiError::Resolver.new(errors)
render json: resolver.to_json, status: resolver.status

Controller Integration

class ApiController < ApplicationController
  include HatiJsonapiError::Helpers

  rescue_from StandardError, with: :handle_error

  def show
    # ActiveRecord::RecordNotFound automatically mapped to JSON:API NotFound
    user = User.find(params[:id])
    render json: user
  end

  def create
    user = User.new(user_params)

    unless user.save
      validation_error = HatiJsonapiError::UnprocessableEntity.new(
        detail: user.errors.full_messages.join(', '),
        source: { pointer: '/data/attributes' },
        meta: { validation_errors: user.errors.messages }
      )

      return render_error(validation_error)
    end

    render json: user, status: :created
  end
end

Custom Error Classes

Domain-specific errors:

class PaymentRequiredError < HatiJsonapiError::PaymentRequired
  def initialize(amount:, currency: 'USD')
    super(
      detail: "Payment of #{amount} #{currency} required",
      meta: {
        required_amount: amount,
        currency: currency,
        payment_methods: ['credit_card', 'paypal']
      },
      links: {
        payment_page: "https://app.com/billing/upgrade?amount=#{amount}"
      }
    )
  end
end

# Usage
raise PaymentRequiredError.new(amount: 29.99)

Functional Programming Integration

Perfect for functional programming patterns with hati-operation gem:

require 'hati_operation'

class Api::User::CreateOperation < Hati::Operation
  ApiErr = HatiJsonapiError::Helpers::ApiErr

  def call(params)
    user_params = step validate_params(params), err: ApiErr[422]
    user = step create_user(user_params),       err: ApiErr[409]
    profile = step create_profile(user),        err: ApiErr[503]

    Success(profile)
  end

  private

  def validate_params(params)
    return Failure('Invalid parameters') unless params[:name]
    Success(params)
  end
end

Configuration

Error Mapping

HatiJsonapiError::Config.configure do |config|
  config.map_errors = {
    # Rails exceptions
    ActiveRecord::RecordNotFound  => :not_found,
    ActiveRecord::RecordInvalid   => :unprocessable_entity,

    # Custom exceptions
    AuthenticationError           => :unauthorized,
    RateLimitError               => :too_many_requests,

    # Infrastructure exceptions
    Redis::TimeoutError          => :service_unavailable,
    Net::ReadTimeout             => :gateway_timeout
  }

  config.use_unexpected = HatiJsonapiError::InternalServerError
end

Available Error Classes

Quick Reference - Most Common:

Status Class Code
400 BadRequest bad_request
401 Unauthorized unauthorized
403 Forbidden forbidden
404 NotFound not_found
422 UnprocessableEntity unprocessable_entity
429 TooManyRequests too_many_requests
500 InternalServerError internal_server_error
502 BadGateway bad_gateway
503 ServiceUnavailable service_unavailable

Complete list of all 39 HTTP status codes →

Testing

RSpec Integration

# Shared examples for JSON:API compliance
RSpec.shared_examples 'JSON:API error response' do |expected_status, expected_code|
  it 'returns proper JSON:API error format' do
    json = JSON.parse(response.body)

    expect(response).to have_http_status(expected_status)
    expect(json['errors'].first['status']).to eq(expected_status)
    expect(json['errors'].first['code']).to eq(expected_code)
  end
end

# Usage in specs
describe 'GET #show' do
  context 'when user not found' do
    subject { get :show, params: { id: 'nonexistent' } }
    include_examples 'JSON:API error response', 404, 'not_found'
  end
end

Unit Testing

RSpec.describe HatiJsonapiError::NotFound do
  it 'has correct default attributes' do
    error = described_class.new

    expect(error.status).to eq(404)
    expect(error.code).to eq(:not_found)
    expect(error.to_h[:title]).to eq('Not Found')
  end
end

Benefits

For Development Teams:

  • Reduced development time with single error pattern
  • Easier onboarding for new developers
  • Better testing with standardized structure
  • Improved debugging with consistent error tracking

For Frontend/Mobile Teams:

  • One error parser for entire API
  • Rich error context for better user experience
  • Easier SDK development

For Operations:

  • Centralized monitoring and alerting
  • Consistent error analysis
  • Simplified documentation

Contributing

git clone https://github.com/hackico-ai/ruby-hati-jsonapi-error.git
cd ruby-hati-jsonapi-error
bundle install
bundle exec rspec

License

MIT License - see LICENSE file.


Professional error handling for professional APIs