ERRORS: Bang, Rescue, Repeat.
Tags: ruby, rails, react, beginners
Published: 2022-12-12 19:54:12

So let's assume that we've got our Rails API all set up, our models in place, and everything is working hunky-dory. Except for when it isn't. Our validations are set up, and they definitely work...but how do we know??

Server-Side Validations define how our application communicates with the server and determines what data is returned. So, let's explore how these validations are sent to the controller and displayed on the *client-side.*

Through our **controller actions** we can use the method `.valid?` inside of a conditional `if...else` statement to determine whether or not our program should render json data, or an error message. We can think of it like this:

- Before saving an Active Record object, Rails runs our validations. If these validations produce any errors, Rails does not save the object.
- We can also run these validations on our own. [valid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Validations.html#method-i-valid-3F) triggers our validations and returns true if no errors were found in the object, and false otherwise.
    - [invalid?](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations.html#method-i-invalid-3F) is the inverse of `valid?`, it triggers our validations, returning true if any errors were found in the object, and false otherwise.

![Example if...else](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zj3l9os6ofwzj3qtx84t.png)

The `create` action is now saying… 

- `if` the parameters that the user passed through as input values to `book_params` are valid and `book.valid?` evaluates as `true`
    - then render the object as created and persist the data to our database.
- `else` if the parameters did not pass our validation, and `book.valid?` evaluates as `false`
    - then the application returns all of the error message as an array of strings
    - and sets the status code to `422 :unprocessable_entity`

The thing that was tough to grasp as a beginner was, using the if...else statement above does not persist our data to the database when a validation fails, but instead returns a _nil object_, or an empty object that looks like this: 

```
=> #<Book id: nil, title: nil, author: nil, description: nil, page_count: nil, year: nil, created_at: nil, updated_at: nil>
```

The important thing to identify here is **id: nil**. Other values may be different, but **id: nil** tells us that this object did not receive an id when it was created, so its data has not persisted to our database. 

…which is confusing. Because even though our instance did in fact fail our validations, we still received what looks like a valid object as a return value. How are you supposed to know if it passed? Check _every single id???_ Of course not, silly goose. That’s where .create!() comes in to _rescue_ us _from_ nil objects!

.create!() does not create an instance object with nil values, but instead raises an exception, or a return value that includes all our validation error messages, which looks like:

```
**ActiveRecord::RecordInvalid (Validation failed: Title can't be blank, Author can't be blank, Description can't be blank, Page Count can't be blank, Year can't be blank, Page count is not a number, Year is not a number)**
```

This return helps us to identify that our instance failed by returning error codes instead of an object that looks like it’s valid, but isn’t. 

We can use `rescue` from within our create controller action to render the exception’s error message as a JSON response

![rescue Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in0g5n1gl9bvbt6qlgpb.png)

ALTERNATIVELY we can refactor and make this cleaner by using the method rescue_from to give specific instructions to our application for what to do when any exception is raised within our model, for any controller action. 

We identify the exception using a specified name (i.e. ActiveRecord::RecordInvalid) and associating a private method called a validation helper and **with**:

![rescue_from example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5c7ernlox43hvy8zog6.png)

Things to note:
- `.record` is a built-in method, used to retrieve the record which did not validate.
- `.errors` is a built-in method, through which we can drill down into the various details of each error. After Active Record has performed validations, any errors found can be accessed through the [errors](https://api.rubyonrails.org/v7.0.4/classes/ActiveModel/Validations.html#method-i-errors) instance method.
    - Returns a *collection of errors*.
    - An object is `valid` if the returned collection is *empty*.

Using method-chaining with `.record.errors`  renders our parameter names (`:title, :author, ...`) and their associated error messages in JSON format. It also sets the status code to `422: :unprocessable_entity`

Here, our helper method is `render_unprocessable_entity()` and takes in a single argument; an object that contains all of the errors that were triggered by the newly created class instance, called an **exception**.  

---

Client-side routing is different than server-side routing - the customer experience is very very different compared to what the chef is doing - though they are, in an abstract way, still connected, they are not directly dependent on one another.  

In order to get our React App to render our error responses, we need to modify the fetch request. For example, 

![POST example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jk9fbs7rafewdgrjhvut.png)

Note: `response.ok` contains a _boolean value_ stating whether the response was successful (status in the range 200-299) or not.

We have to use `Object.entries` to create our mappable array in this case because of how our `errors` data is *nested* inside of an *array*, inside of an *object*, inside of a _**hash.**_ 

- Data is nested in a hash with errors listed individually, and their messages as arrays…
    
    `{errors: {”title”: [”can’t be blank”]}, {"author": ["can't be blank"]}, ...}`
    
- Because of this, if we call `Object.entries` on our errors, it turns our content into an _array of arrays_ with the key of a particular value in the first index and the error message in a nested array that looks like:

```
Object.entries(data.errors) --> 

[[title, ["can't be blank"]], [author, ["can't be blank"]], [page_count, ["can't be blank", "is not a number"]]...]
```
- With this nested array, we can call `.map` on each individual entry and use string interpolation to create an array of errors as strings that we can map through, using their index.

```
.map(error => `${error[0]}: ${error[1]}`)

errors = ["title: can't be blank", "author: can't be blank", "page_count: can't be blank,is not a number"...]
```
- We can then use a conditional ternary statement within our component to display the error messages, if any exist.
- This newly created `errors` array is then set to *state* using our `setErrors` function, which causes our component to re-render
- When the component re-renders, it checks the `errors` state array to see if there’s anything inside of it
- If there is, it renders each item in the `errors` array inside of a `<div>` element

And that's all there is for errors! From server to client side, hopefully that all makes some kind of sense. 

If you have any questions or recommendations for edits, feel free to reach out. I welcome any and all feedback.

Keep fightin' the good fight! ʕ•ᴥ•ʔ