0

I am implementing a REST API and need to validate the JSON API inputs for the CREATE and UPDATE endpoints. The goal is to send a 400 error prior to doing any processing if the inputs are not valid.

In the past, like most people I have been using some sort of schema validation tool such as Marshmallow/Cerberus/Schema/Voluptuous/Schematics (I'm using Python here)

But my DB models themselves can (and should?) contain validators and/or constraints to guaranty the consistency of the data in my database.

With that in mind my general strategy for record creation looks like this:

create_record(inputs):
    errors = validate_schema(inputs)
    if errors:
        send_400(errors)

    try:
        record = create_record(inputs)
    catch:
        send_500()

    return record

So it sounds like I need to perform some kind of data validation in two places which has some potential downsides:

  • Need to maintain code in two different places
  • If my schema validation is ever more permissive than the DB validators/constraints, the validation will pass but the record creation will still fail and will result in a 500 instead of 400.

Is there some DRY strategy that is recommended for this?

2

I would advise against HTTP 400 for syntactically correct (here a 400 would be misleading), but semantically incorrect (aka invalid) request. But that should not be the topic here.

The question is more about validation and DRY.

I think, there is no golden rule to follow here. If you are doing a modern web application, there is a tradeoff between double book-keeping, DRY and maintainability.

The typical stack has:

  • A frontend for the browser (which needs to implement some kind of validation)
  • A backend (which needs to implement some kind of validation)

And in your case, you have validation in your code and in your DB.

For ease of use - from a user's perspective - it makes sense to have a validation layer on the client, ready to interactivly indicate errors and give advice how to improve the input. Gone are the days, where you filled out your Form, sent to the server and had to re-enter everything because there were errors.

That said, there is always - at least some kind of - double book-keeping between client and server. I saw people trying to write frameworks including abstract languages which kept the business logic in one place and used code generators to generate clientside and serverside code to prevent DRY at least from a logical point of view. Which ended in a hard to grasp and hell to maintain software.

Coming to the question about DRY in the backend. It would make sense to split your validation logic into two parts:

1) things a database could handle best:

  • Constraints (»Is this thing unique?«, »Does this thing refer to something known?«)
  • Datatypes (»Is this really a date?«)
  • (maybe data access - if you implement users equaling DB users)

2) things your code does best:

  • Validating postal codes
  • Checking for completeness etc.

This leads to roughly two classes of errors emitting from your data access layer:

  • technical errors (the DB is gone)
  • semantical errors (unique key constraint violation)

The latter would be transformed into a validation error.

In case of datatypes you would end up with some kind of double checking - e.g. whether something is a date - which would be neglectable. And for the rest it is orthogonal / relatively DRY.

BUT: Depending on your consistency requirements If you see your database as a kind of last line of defense you would implement more validation logic in the code as well as in the database (which would undermine DRY).

0

The dry way would be do handle multiple types of errors from create_record rather than do validation for each different type of validation error there can be. For example

try:
  record = create_record(inputs)
catch InputsValidationError:
  send_400()
catch:
  send_500()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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