One of the most common problems in web applications is allowing invalid data into the database. Incorrect data can lead to bugs, inconsistent records, and security issues.
To prevent this, developers implement validation in both the frontend and the backend. Frontend validation improves the user experience by catching errors early, while backend validation ensures that only valid data is stored in the database.
In this article, we will explore how validation can be implemented in React using Formik and Yup, and how similar protections can be applied in the backend using Flask and SQLAlchemy.
Formik
Formik is a library used in React to manage form state, validation and submission without having to manually write the whole logic by ourselves.
The following example shows a form that allows a user to create a new store.
We use Formik to handle the form state and submission logic, and Yup to define a validation schema that ensures the user enters valid information before the request is sent to the backend.
Below is a visual example of the form validation in action.
Looking at this example, we can see at the top the imports for useFormik and the Yup library. The useFormik hook handles the form state, while Yup is used to define our validation schema and the rules for our attributes.
For example, the name and address fields are required and must meet a minimum and maximum number of characters. The phone number field is also required, and it must match a valid phone number format.
Inside useFormik we initialize the form configuration. The initialValues object defines the default values of the form fields. Then we connect our formSchema, which links the Yup validation rules to the form.
Finally, we define the onSubmit function. This function is responsible for sending a POST request to the backend with the form data. If the request is successful, the new store is added to the user's list of stores in state and the form is reset.
Even though validating data in the frontend helps prevent bad input, it is not completely secure. Frontend validation can be bypassed by sending requests directly to the server. Because of this, developers must also implement validation in the backend.
Validation in the backend
There are several ways we can protect our data in the backend. Two common approaches are enforcing constraints on database columns and creating custom validations in our models.
Constraint validations
Enforcing constraints on database columns guarantees that only appropriate data is saved in the database. Some of the most common constraints are:
Nullable: ensures that the value of a column cannot be null. We can implement this by defining the column with
nullable=False.Unique: ensures that the value stored in a column must be unique across the table. This constraint is applied by defining the column with
unique=True.
In the example below we can see the implementation of nullable in a model:
In the example above we observe that by assigning nullable=False to the column we avoid users from submitting inputs without the required value.
While applying database constraints helps to enforce basic rules, there are cases where we require custom validation. SQLAlchemy enable us to create custom validation by using the @validates decorator.
@validates
The @validates decorator is used to create custom validation rules for specific model attributes. This validation runs when a value is assigned to the model attribute, before the data is committed to the database. If the data is invalid, an error is raised and the record will not be saved.
Below is a simple example showing how the @validates decorator can be used to validate a phone number before saving it to the database.
from sqlalchemy.orm import validates
class Store(db.Model):
__tablename__ = "stores"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
address = db.Column(db.String, nullable=False)
phone_number = db.Column(db.String, nullable=False)
@validates("phone_number")
def validate_phone_number(self, key, phone_number):
if len(phone_number) < 7:
raise ValueError("Phone number must contain at least 7 digits")
return phone_number
In this example, the @validates decorator ensures that the phone_number field contains at least seven characters. If a shorter value is given, a ValueError is raised and the record will not be committed to the database.
Implementing validation in our programs plays a big role in the development of our applications. While using validation in the frontend improves the user experience by catching errors early on, the backend validation avoids bad data being committed in the system even if it passes the frontend check. By combining both frontend and backend validation strategies, developers can build more reliable and secure applications.




Top comments (0)