Hyrum Carlile
Hyrum Carlile

Reputation: 117

Return Granite validation errors as JSON

I have a Granite User model with some validations. When someone makes a POST request to users/new, I'd like to return the validation errors (if any) as JSON. Currently, I have:

if user.errors.size > 0
  halt env, status_code: 500, response: user.errors.to_json
end

But when I try to compile, I get:

in /usr/local/Cellar/crystal/0.26.1/src/json/to_json.cr:66: no overload 
matches 'Granite::Error#to_json' with type JSON::Builder
Overloads are:
- Object#to_json(io : IO)
- Object#to_json()

  each &.to_json(json)
         ^~~~~~~

Upvotes: 0

Views: 98

Answers (1)

pixatlazaki
pixatlazaki

Reputation: 561

So the issue is that User#errors is an Array(Granite::Error), i.e. an Array holding Granite::Errors. Unfortunately, it doesn't look like Granite::Error implements the to_json(JSON::Builder) method (i.e. the method to_json taking a parameter of type JSON::Builder), which Array#to_json relies on (that snippet you see there is from the implementation of Array#to_json which you can view on GitHub.).

I'd recommend building the JSON yourself using JSON.build. This has the added side-effect of keeping the JSON that you respond with (which I imagine is being consumed by some client) is entirely in your control. If the developers of Granite were to change how they encode Granite::Errors in JSON and you were using their to_json method, the change wouldn't raise anything at compile time.

As a side-note, I'd recommend against using a status code of 500 to denote a validation error, as that is typically reserved for unexpected errors internal to your server. A 4xx error (e.g. 400 - Bad Request) would be more appropriate. As a second side-note, it would be more RESTful to have the POST be made to the /users endpoint, as opposed to /users/new.

With these changes, here would be the resulting snippet:

if user.errors.size > 0
  errors_as_json = JSON.build do |json|
    json.array do
      user.errors.each do |error|
        json.string error.to_s
      end
    end
  end
  halt env, status_code: 400, response: errors_as_json
end

Upvotes: 1

Related Questions