
Grape is a popular Ruby framework for building RESTful APIs. Exception handling plays a crucial role in ensuring the stability and reliability of any application, including those made with Grape.
This article will explore the basics of Grape exception handling, including customizing exceptions. We'll also touch on some best practices, and how to integrate your app with AppSignal for enhanced error monitoring and management.
Let's get started!
Basics of Grape Exception Handling
In this tutorial, we’ll see how to handle exceptions in a Grape API built in Rails. I have made a demo job board API for this, and you can check out the source code on GitHub.
Raising an Exception
You can raise an exception in Grape by using error!. For example, in the job API mentioned above, we have a show route that returns a job based on the ID. We can return a 404 error when the record is not available, like this:
get do
if job
present job
else
error!('404 Not Found', 404)
end
endWhen you raise an exception, you’ll want to handle it in a “unique” way — you most likely will not want to send the raised exception to your users.
In Ruby, we have a default mechanism for exception handling. It works by wrapping code that might raise an exception in a begin block. The rescue block is used to handle the exception that has been raised.
begin
#... process, may raise an exception
rescue =>
#... error handler
else
#... executes when no error
ensure
#... always executed
endSo, here is what that will look like in a typical scenario:
begin
File.readlines('input.txt').each { |line| values << Float(line) }
rescue Errno::ENOENT
p 'file not found'
rescue ArgumentError
p 'file contains unparsable numbers'
else
print values
endThe rescue_from Method
When you raise exceptions, or they happen without your direct involvement, you’ll want to handle them properly.
By default, Grape provides a rescue_from method. This allows you to specify a block of code that gets executed when defined exceptions are raised.
So, to “rescue” or handle the 404 error we raised before any other one that arises in the jobs resource, we can use the rescue_from method. The method is added above the jobs resource.
# Rescue 404 errors
rescue_from :all do |error|
error!({ error: error.message }, 404)
end
# Jobs resource
resource :jobs do
desc 'Return list of jobs'
get do
...
end
...
endWe can also specify the content type to be used:
rescue_from :all do |error|
error!({ error: error.message }, 404, { 'Content-Type' => 'text/error' })
endThis way of handling an exception is too generic — we are rescuing every form of exception and returning an error with a 404 status code. That is misleading if our API users expect to get a 400 status code.
We can instead specify the exception we want to handle:
rescue_from ActiveRecord::RecordNotFound do |error|
error!({ error: error.message }, 404, { 'Content-Type' => 'text/error' })
end
rescue_from :all do |error|
error!({ error: error.message }, 500, { 'Content-Type' => 'text/error' })
endWhen we encounter an ActiveRecord::RecordNotFound error, we’ll return an error message with a 404 status code. Otherwise, we’ll return an error message with a 500 status code.
This shows that we can improve on what we currently have, but what if we want an error handler that rescues from all errors? That's where customizing exceptions comes in.
Customizing Exceptions in Grape for Ruby
Depending on the type of error encountered, this error handler should be able to return an error message alongside the correct status code.
First, create a file called exceptions_handler. Then, we’ll move our current exception handlers into the file:
# frozen_string_literal: true
module V1
module ExceptionsHandler
extend ActiveSupport::Concern
included do
rescue_from ActiveRecord::RecordNotFound do |error|
error!({ error: error.message }, 404, { 'Content-Type' => 'text/error' })
end
rescue_from :all do |error|
error!({ error: error.message }, 500, { 'Content-Type' => 'text/error' })
end
end
end
endOur ExceptionHandler module uses ActiveSupport::Concern , allowing us to access functionalities like included and class_methods. In the snippet above, we have the error handlers in the included block, so wherever this module is included, they will be available as they’re defined.
We can go ahead and remove the error handler from the files where we had them previously. Then we can include the ExceptionsHandler module in our API entry file — api.rb:
# frozen_string_literal: true
module V1
class API < Grape::API
include ExceptionsHandler
mount V1::Jobs
end
endLet’s create a base error class for our errors. This class will be responsible for returning the error response.
module V1
module Exceptions
class BaseError < StandardError
attr_reader :status, :message
def initialize(message: nil, status: nil)
@status = status || 500
@message = message || "Something unexpected happened."
end
def body
Rack::Response.new({ error: message }.to_json, status)
end
end
end
endThe class accepts two keyword parameters: a message string and a status. If none is passed, we’ll use the default.
In the body method, we return a Rack response. By default, the rescue_from handler must return a Rack::Response object, call error!, or raise an exception.
We can go ahead and make use of it in the ExceptionsHandler:
included do
rescue_from ActiveRecord::RecordNotFound do |error|
error!({ error: error.message }, 404, { 'Content-Type' => 'text/error' })
end
rescue_from :all do |error|
Exceptions::BaseError.new(message: error.message).body
end
endWhen we call the /error endpoint, we’ll see oops returned as the response. At this point, we can create a class for NotFound errors.
module V1
module Exceptions
class NotFound < BaseError
def initialize(message: nil)
super(
status: 404,
message: message || "Oops, we could not find the record you are looking for."
)
end
end
end
endThe NotFound class only accepts message. Since it inherits from BaseError, we need not return a Rack::Response again.
We can go ahead and use it in the ExceptionsHandler like this:
included do
rescue_from ActiveRecord::RecordNotFound do |error|
Exceptions::NotFound.new(message: error).body
end
rescue_from :all do |error|
Exceptions::BaseError.new(message: error.message).body
end
endNow, if we attempt to raise an error like this manually:
raise Exceptions::NotFound.new(message: "Something unexpected happened.......")This will work fine, but the status code will be 500, because it returns the response in the BaseError class (as the BaseError class handles the error).
To fix that, we’ll need to modify the ExceptionHandler to explicitly use the NotFound class to handle the error instead.
So, whenever an error corresponding to ActiveRecord::RecordNotFound and V1::Exceptions::NotFound is encountered, use Exceptions::NotFound. Otherwise, use Exceptions::BaseError.
included do
rescue_from ActiveRecord::RecordNotFound do |error|
Exceptions::NotFound.new(message: error).body
end
rescue_from V1::Exceptions::NotFound do |error|
Exceptions::NotFound.new(message: error.message).body
end
rescue_from :all do |error|
Exceptions::BaseError.new(message: error.message).body
end
endYou can see that we’ll need specificrescue_fromblocks as we create more error classes. We can improve this by using case statements:
module V1
module ExceptionsHandler
extend ActiveSupport::Concern
included do
rescue_from :all do |error|
case error.class.name
when 'ActiveRecord::RecordNotFound', 'V1::Exceptions::NotFound'
Exceptions::NotFound.new(message: error.message).body
else
Exceptions::BaseError.new(message: error.message).body
end
end
end
end
endEt voilà!
Best Practices and Tips
While there are tons of best practices that you can employ for exception handling, here are a few quick tips to follow:
- Group related exceptions: As we saw in the code above, grouping related exceptions allow us to have maintainable code. As the number of exceptions we want to handle increases, we can add them to our list.
- Use helpers like
error!to quickly raise exceptions. This simplifies your exception handling. - Make use of exception monitoring tools like AppSignal.
AppSignal Integration: Grape for Ruby
AppSignal helps you to monitor and track errors in your applications. Integrating AppSignal with your Grape API gives you valuable insights into exceptions. This guide shows you how to integrate AppSignal with your Grape API. Whenever an error occurs in your API, you’ll see it in your AppSignal dashboard, like so:

Wrapping Up
Exception handling is a critical aspect of developing robust APIs. In this tutorial, we’ve seen how to properly handle exceptions in a Grape API. We also briefly looked at some best practices and AppSignal's integration for Grape.
Exception handling is an ongoing process — one you’ll need to improve on consistently.
Happy coding!
P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!
P.P.S. Did you know that AppSignal offers an Active Record integration? Find out more.
Published
Wondering what you can do next?
- Subscribe to our Ruby Magic newsletter and never miss an article again.
- Start monitoring your Ruby app with AppSignal.
- Share this article on social media

Kingsley Chijioke
Our guest author Kingsley is a Software Engineer who enjoys writing technical articles.
All articles by Kingsley ChijiokeBecome our next author!
AppSignal monitors your apps
AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!


