DEV Community

Nic Mortelliti
Nic Mortelliti

Posted on

Rails Validations

Rails Validations Notes

When creating databases, we want our data to be valid, in other words, we expect the data in our database to be of a certain type and format in order to ensure our we can interact with the data in a predictable manner.

Introduction

For example, if we are storing the email addresses of our users, we expect those email addresses to contain certain syntactic features. As Mishel Shaji describes, the email address should contain a username (e.g. "johnsmith"), the @ symbol, the domain name (e.g. "gmail"), a dot (".") and the domain extension (e.g. "edu"). The username, domain name and domain extension must adhere to specific syntactic requirements as determined by the Internet Official Protocol Standards. By the way, if you feel like you need some more technical, non-fiction reading in your life, go check out the Request For Comments (RFC) regarding email address standardization (among other topics) in RFC 5322 and RFC 6854.

In order to make sure an email address in our database aligns with these standards, we need to use validation. We could, of course, pretend we're fluent in speaking Martian and write a bunch of regex code like that found in this StackOverflow answer:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

I'm sure there are completely valid situations where regex is the ideal approach to validation. However, in the case of my Ruby on Rails project with the Flatiron School, Rails' built-in validation macro was the preferred approach.

Thankfully, one of the benefits of developing our back end with Rails is that it has built-in validation tools to do the heavy-lifting, allowing us to keep our code lean and readable.

Rails Validations

The Ruby on Rails guide describes validations as a way

to ensure that only valid data is saved into our database.

Therefore, utilizing Rails validation tools, if our user attempts to create an account with an email address of joe@smith@aol.com, Rails won't save the new entry into the database because it doesn't adhere to standard email address syntax due to the multiple uses of the "@" symbol.

How to include validations in your Rails project

Validations can be included in your Rails project by explicitly using the "validates" keyword in the model, specifying which table attribute is receiving the validation ("email") as well as the type of validations being performed on the attribute ("format", "presence" and "uniqueness").

validates :email, format: { with: URI::MailTo::EMAIL_REGEXP },
                  presence: true,
                  uniqueness: { case_sensitive: false }
Enter fullscreen mode Exit fullscreen mode

How to handle invalid data

If a validation fails (i.e. returns "invalid"), we can collect all the error messages produced during the validation phase! This is useful, not only to developers during development for debugging, but it's also useful for users of our applications. We can pass the full array of error messages to the front end for display in alert messages, for example.

Back to our email validation example, we can include our validation error message array in the JSON object we return to the front end like this:

render json: { errors: e.record.errors.full_messages }, status: :unauthorized
Enter fullscreen mode Exit fullscreen mode

In this example, the error messages returned contain the database attribute names (e.g. If no email address is entered and submitted to the API, the "errors" key of the JSON object will contain an array with the message "Email is blank".).

If our React front end doesn't receive a status of "ok" from the API, we can save the errors to state using the useState hook, then map the error messages to user-facing alerts.

fetch("/login", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: formData.email,
    password: formData.password,
  }),
}).then((response) => {
  setIsLoading(false);
  if (response.ok) {
    response.json().then((user) => onLogin(user));
  } else {
    response.json().then((err) => setErrors(err.errors));
  }
});
Enter fullscreen mode Exit fullscreen mode

What if I don't want to run validations for a particular API action?

It's possible to skip a validation for a particular action. Be careful when doing this, because we may end up saving corrupt or incorrectly formatted data to our database. Check out section 1.3 of the Rails Guide on Validations to learn which methods skip validation.

Conclusion

Rails' built-in validations are great way to reduce the amount of code you need to write in order to ensure you are maintaining a healthy database with predictable data. For more information on Rails validation methods, check out the Active Record Validations guide.

If you've worked on projects involving databases, how does your group handle the validation of data? Are there methods that are pretty standard from organization to organization?

Top comments (0)

The discussion has been locked. New comments can't be added.