4

Here we go again with topic what was asked many times all over internet, but I not found any good enough answer:

What are best practices in making client and server side validations synced?

I was not doing it in web apps I was making (I just had duplicated validations), but now when I started with React Native and REST API as backend I asked myself if I have nerves to do every validation twice and edit it twice if something change.

Options what are getting into my mind:

  1. Not share validation at all (Have two validation rules, two validator functions, two regexes set...)
  2. Have just server side validation (so much 2000's am I right?)
  3. Having just client side validation (I AM JOKING!)
  4. Let client app download at start some rules/sets from server and use them

I was really thinking about the 4th option, but maybe its too complicated (regexes, functions, etc. cannot be moved easily trough internet - JSON for example), but it is also good for on-air updates etc.

I really dont know.

Any ideas, tips, best practices?

4 Answers 4

1

The strategy that I've implemented involves the use of json-schema. You create a single JSON schema like the example below that can either be stored on a CDN or locally by having your build tools place a copy in the dist directory of your backend and frontend codebases.

Example JSON Schema

{
    "$schema": "https://json-schema.org/draft/2019-09/schema#",
    "$id": "https://cdn.example.com/schemas/profile.json",
    "type": "object",
    "properties": {
        "logo": {
            "sm": {
                "type": ["string", "null"],
                "pattern": "^([A-Za-z0-9\\-_.]|)$"
            },
            "md": {
                "type": ["string", "null"],
                "pattern": "^([A-Za-z0-9\\-_.]|)$"
            },
            "lg": {
                "type": ["string", "null"],
                "pattern": "^([A-Za-z0-9\\-_.]|)$"
            }
        },
        "name": {
            "type": ["string", "null"],
            "minLength": 1,
            "maxLength": 64
        },
        "title": {
            "type": ["string", "null"],
            "minLength": 1,
            "maxLength": 64
        },
        "email": {
            "type": ["email", "null"],
            "maxLength": 255
        }
    },
    "required": [
        "name",
        "title"
    ]
}

Frontend Validation

In my Angular app I created a service that pulls in the schema and refactors it so that it can be fed directly into FormBuilder.group() as a configuration object. I suppose I could post an example if someone needs it but since you're using React it probably won't be helpful for this question.

Backend Validation

For my Express backend it does the following:

  1. Retrieves the JSON schema.
  2. Passes the payload and schema to the validate() function of json-schema.
  3. If valid, performs extended validations if necessary and then processes the data. If invalid, returns the errors from the validate() function.

Summary

It's true that the frontend and backend will have different validation requirements but the strength of this strategy is that your basic frontend validation rules will always match your backend validation rules resulting in fewer bugs and better security. It also allows you to implement additional validation checks on the backend that shouldn't be implemented on the frontend.

2
  • @Nachiappan-Kumarappan mentioned some drawbacks of an approach like this in his answer. I would further argue that this only applies to the very simplest form of validation, stuff that hardly even matters. Important business rules are often more complex and it will be hard to capture in a scheme like this. For example 'email is required for users from country x' or 'amount of ordered items can only be changed until the next business day 6pm'.
    – Rik D
    Commented Nov 27, 2020 at 8:11
  • 1
    You may be overlooking the value of this strategy. This is just the top layer of validation, the boring sanity checks that need to be performed on the front and back end. If “name” has a max 64 chars in the front, the back needs the same rule. If the frontend uses Angular Validators and the backend uses Joi, you have two completely different implementations for the same rules. This strategy homogenizes that first basic level of validation that you can then add more stringent validations to avoid security issues where a field might be erroneously required on the front but not on the back Commented Nov 27, 2020 at 8:40
1

json-schema and its extension are good to have, you have the schema definition at the beginning. It is better to have a single definition of validations in one place on the server that can then generate appropriate JS for client-side and AJAX-based validations.

1

Yes!!! This could be a bit annoying. I too find no perfect solution.

Now that your have listed all possible options, let me give you my opinion on them:

  • Option 3: "Having just client side validation (I AM JOKING!)"

Glad you were only joking :)

  • Option 4: "Let client app download at start some rules/sets from server and use them"

As you rightly said, this will push the complexity. We could have a different kinds of validations such as "lessThan", "lessThanOrEqualTo", "greaterThan", "inBetween", "regex" and so on... So if the client has to download all this validation rules from server and validate, we need to have a schema for the validation rules. This sounds too complex to me. I wouldn't want to do this as we are not building a validation framework. I am okay with a bit of duplicate code in favor of reduced complexity.

  • Option 2: "Have just server side validation"

I think this would be a nice idea, as there is no duplicate code (validation not in two places and two languages). The cost of this is few extra Http calls. I think this would be okay in most business scenarios. If the user experience is bad in this option, then move towards option 1

  • Option 1: "Not share validation at all"

This is also okay. However over time when the validations change, a good effort is needed to keep the back end and front end validations in sync. This would increase the probability of bugs.

I would prefer to start with option 2, move towards options 1 if necessary on case by case basis.

For the specific case of Node js back end, you could share the validation logic between the front end and back end.

As WebAssembly is getting popular, you could try to use WebAssembly for validation. This way it is possible to execute Java or C# or few other language code in the browser. (I haven't explored this option much)

1

Interesting topic, pretty much every web application copies those rules, so such a library would be very much welcome ;)

Here's an example implementation using ASP.NET Core and Angular: https://devblogs.microsoft.com/premier-developer/angular-how-to-share-server-side-validation/.

Note that another way to implement this could be to just use the same language on the backend and the frontend. JavaScript and TypeScript are no-brainers but you might also execute backend languages in the browser using WebAssembly. Here's an example of integrating FluentValidation with Blazor WebAssembly: https://blog.stevensanderson.com/2019/09/04/blazor-fluentvalidation/.

Not the answer you're looking for? Browse other questions tagged or ask your own question.