JSONError
Installation
Add this line to your application's Gemfile:
gem 'json_error'
And then execute:
$ bundle
Introduction
What is JSONError gem?
It provides parent class with simple DSL to define API errors. It is based on concept, that error is something that went wrong (what? = key), in some circumstances (when? = context).
Why would I use it?
To design, manage and maintain API's errors JSON responses with ease.
How to do it?
Using locale file (e.g. json_errors.en.yml).
# json_errors.en.yml example
en:
json_errors:
resource_not_found:
status: 404
message: 'Resource %{name} (%{id}) was not found.'
description: 'Blah blah blah.'
href: 'https://developers.api.pl/doc/errors/resource_not_found'
batmans:
resource_not_found:
status: 404
message: 'Resource Batman (%{id}) was not found.'
description: "Cuz I'm Batman"
Usage
Understading JSONError
Let's take a look at example error class definition.
class Error < JSONError.base
key :error
translated_properties :message, :href
custom_properties :details, :some_dependency
visible_properties :key, :status, :message, :href, :details
end
Key is the error identifier. It's not obligatory to specify its value here - you can also and pass it to the Error initializer when instantiating error object:
Error.new(key: :error)
however, json errors must have a key. Key is not inherited.
Default properties are properties included to JSON output by default (:key, status: 500). You can change status in locale.
Translated properties are error attributes which values will be loaded from locale. Translated properties can be inherited.
Custom properties are attributes that won't be loaded from locale but passed to JSON output as they stand. It can be for exammple hash of ActiveRecord object validation error messages. Custom properties can be inherited.
Visible properties is optional whitelist for output JSON properties. By default all default, translated and custom properties are included in output. You can use visible_properties method to include only demanded subset. Visible properties are not inherited.
Defining JSONError
Let's introduce two example error classes now. They will be used in further examples.
module Errors
class ResourceNotFound < JSONError.base
key :resource_not_found
translated_properties :message, :href, :description
end
class OtherError < JSONError.base
key :other_error
translated_properties :message
end
end
Defining JSONError Properties
Just add entries to locale.
# config/locales/json_errors.en.yml
en:
json_errors:
resource_not_found:
status: 404
message: 'Resource %{name} (%{id}) was not found.'
description: 'Blah blah blah.'
href: 'https://developers.api.pl/doc/errors/resource_not_found'
Rendering JSONError
Rails
# app/controllers/batmans_controller.rb
class BatmansController < ApplicationController
def index
error = Errors::ResourceNotFound.new(name: :Batman, id: 2)
render json: error, status: error.status
end
end
# or
class BatmansController < ApplicationController
def index
raise Errors::ResourceNotFound, name: :Batman, id: 2
rescue JSONError.base => e
render json: e, status: e.status
end
end
Sinatra
# Remember to load dependencies
class Application < Sinatra::Base
before { content_type :json }
get '/batmans' do
error = Errors::ResourceNotFound.new(name: :Batman, id: 2)
halt error.status, json(error)
end
end
Responses
For ResourceNotFound
{
"status": 404,
"key": "resource_not_found",
"message": "Resource Batman (2) was not found.",
"description": "Blah blah blah.",
"href": "https://developers.api.pl/doc/errors/resource_not_found"
}
For OtherError
{
"status": 500,
"key": "other_error",
"message": null
}
Using Contexts
You can specify different contexts for the same error key.
# In locale
en:
json_errors:
resource_not_found:
status: 404
message: 'Resource %{name} (%{id}) was not found.'
description: 'Blah blah blah.'
href: 'https://developers.api.pl/doc/errors/resource_not_found'
batmans:
resource_not_found:
status: 404
message: 'Resource Batman (%{id}) was not found.'
description: "Cuz I'm Batman"
# In constructor
Errors::ResourceNotFound.new(context: 'batmans', id: 2)
// And then response will look like below
{
"status": 404,
"key": "resource_not_found",
"message": "Resource Batman (2) was not found.",
"description": "Cuz I'm Batman",
"href": null
}
Contributing
Questions, propositions, bug reports and pull requests are welcome.
License
The gem is available as open source under the terms of the MIT License.