Press "Enter" to skip to content

MERN Stack & GraphQL – #7 Server-Side Validation


hey guys welcome back to cold room my
name is Alex and in this video we’re
gonna be talking about validation so
validation is all about three basic
things first of all it’s about enforcing
business logic so it’s making sure that
the input provided by the users actually
complies with a given set of rules and
validation formats do you expect it to
adhere to the second thing is of course
preserving data integrity so we want to
make sure that the data stored in our
database what was the same exact format
across the board and the last one is to
avert malicious inputs so of course we
can’t trust any of the input provided by
the client so we have to make sure that
that April doesn’t harm our systems or
doesn’t alter the way that our
application is working so when it comes
to validation and a graph QL API service
there’s of course several approaches
that we might consider one of them has
to do with graph QL directives now graph
QL directives are special language
constructs and they allow you to apply
logic to your queries so for example the
graph QL specification by Facebook comes
with two built-in directives one is
include and the other one is skip so
this one allows you to conditionally
include a sub selection of fields’ only
query depending on the boolean so if the
boolean turns out to be true then you
want to select that sub selection of
fields’ and then the skip directive
works the exact same way except if the
boolean is true you want to skip the
selection of fields’ now the include and
skip directives in the spec are really
only just the example and of course we
could create custom directives with a
polo and in fact there is a package
called graph QL constraint directive
let’s actually look into it what this
one does is it defines a custom
constrained directive it allows you to
apply custom validation like minimum
length maximum length you could also
apply format so for example for an email
address you can apply the email format
you also have a bunch of other things so
for example checking for characters that
the string contains checking for a
specific pattern using regular
expressions so this is quite a versatile
approach but the disadvantages are that
well first of all this package doesn’t
actually allow you to define the
constrained erectus on Cori arguments so
what I mean by that is you can’t
actually have a let’s say mutation you
can’t put the constraint directive and
next to the actual arguments to that
mutation so what ends up happening
oftentimes is you create a separate
good type with all of the arguments that
your mutation expects and then you can
attach the constrain directive next to
them but if you wanted to list all of
the arguments you can’t basically
specify the constraint next to them so
that was one disadvantage and the other
one and probably the more important one
for us is that unfortunately this
package is not compatible with a Polo
server v2 and even if you make it work
with the backwards compatible wee one
you will actually get weird quirks with
that library now this package has
actually been featured in a pool blog
and like I said it does work for the
most part with a polo v1 but once again
you can sometimes run into weird
behaviors with that version so because
we’re gonna be using a polo version two
we’re gonna have to look into
alternatives to graph QL constrain
directive so the next approach that we
can look into is do a validation inside
of resolver functions now what that
basically means is you can go ahead and
put the validation logic inside of your
resolver functions and in fact if we go
back to our project I’m going to go back
to the user resolver we have a query for
a user so we’re trying to find a user by
ID first of all have to check that the
user ID provided by the client actually
matches the Mongoose object ID type so
we have a snippet of custom validation
inside of the resolver function if that
validation check fails then we’re
basically throwing a built-in user input
error exception object and so this is
one example of how you can do a
validation inside of your resolvers so
this approach is very simple and
intuitive putting the validation logic
inside of the resolver functions would
probably be the first thing you would be
child to do but of course there’s quite
a few disadvantages and first of all
this approach is very repetitive so you
can imagine if you create more resolver
functions or more objects for different
models you can have the same type of
validation across the different files so
it quickly starts to violate the DRI
principle it also tends to clutter up
very quickly and it doesn’t actually
scale beyond a few simple validation
checks so the more validation checks you
add the more complex and more elaborate
your validation becomes inside of this
file so that has been resolver functions
well what else can we do let’s say we
decide that resolver functions are not
optimized enough so we could then look
into utility functions so what you can
do is you can create
your functions using a library like
validator j/s so if we look at this
library this one actually has quite a
few useful methods so for example it has
an export of is email method if I look
at it this one actually takes in an
email address and it checks if the
argument actually complies with the
email format so we also have things like
is empty we also have is Mongo ID so
this one checks if the argument is in
fact a mobile DB object ID argument
there’s quite a few other things as well
so for example checking for a phone
number checking the length of a string
is actually a very vast selection of
methods you could pick from so using our
code base as an illustration what we
could do is we can basically take out
this logic this if condition and we
could create a utility function so for
example we could call it is object ID
we’ll get taken the ID arguments in
question and we can return an arrow
function we can basically have the if
condition inside of it so what we can do
then is we can return either true or
false depending on that condition or we
could simply do the check and then if
the check fails we could just throw out
the exception itself so this would be
one way to do it and of course you could
take out this function for instance we
could create a utils file we could also
create a function for it so let’s do for
example functions yes this would be sort
of like utility functions that you could
reuse across the project so we could
have an export of that function is
object ID and then inside the resolver
function you can basically call it like
this passing in the ID argument inside
I’m actually going to bring it back for
the time being so let me get rid of
those utils folder so back in your notes
this approach of course is simple enough
and it’s also quite composable so you
could actually turn those functions into
higher-order functions you can actually
chain them one after another and this
approach can actually scale so it’s a
little bit better than the previous one
now the concept is approaches that we’re
actually pretty much reinventing the
wheel what I mean by that is that
there’s actually already validation
libraries out there so we’re trying to
rule out our custom solution there’s
gonna be downsides associated with that
and the second disadvantage is that of
course this is more code to maintain
because we’re creating custom functions
or validation we’re getting
to maintain all that code and we’re also
gonna need to probably write unit tests
for it so it’s basically only more code
for us to take care of in our project so
let’s say we want to move on beyond the
utils will be the alternative way out to
utility functions well we could actually
take advantage of the built-in
validation and Mongoose well as it turns
out Mongoose has an OD M and as many
other RM libraries as well actually it
comes with a lot of built-in validation
methods that we can take advantage of so
for example for number types we have the
validation for a minimum value of a
number maximum value for Strings that we
also have enums so for example you could
whitelist
a set of values that you accept for a
string you can also use the required
attribute and of course we have a bunch
of other ones like lower case upper case
we have min length maximum length match
for rejects expressions it was actually
quite a few of them that you could use
this approach also turns out to be quite
extensible so you could actually define
custom validation logic with custom
validators and you could actually
customize the error messages themselves
so some of the downsides with this
approach as I find is there’s actually
quite a few gotchas in Mongoose so first
of all you already saw the unique
property it turns out it’s not actually
evaluator but it’s in fact a unique
index this is something we talked about
a few videos ago so you might want to go
back and check that one out but then the
other thing is that the update
operations so for example update one or
update mini well those methods actually
have validation turned off by default so
what we’ll have to do is we’ll have to
pass in an option and manually turn on
the validation if I wanted to be applied
for our update operations and the other
caveat is that a lot of those updates
methods or operations don’t actually
trigger a lot of pre and post hooks and
this might be something to be mindful of
as well now the other thing that I
follow up with Mongoose is that it’s
actually pretty hard to validate a
subset of fields so what ends up
happening oftentimes is that you have a
model let’s say a user model which has
an email field user name name and
password let’s say the user is trying to
reset the password so you want to update
the user model but really only update
the password field so we’re gonna try to
do a find by ID you’re gonna try to call
the save method
turns out the save method is actually
going to try to run validation on all
the fields of your model so if some of
those fields have for example unique
constraints so let’s say the email
cannot be duplicated
well those constraints are actually
going to fail now there’s ways around
that but once again this is one of the
caveats that we have to be mindful of so
like I said validating a subset of
fields is really not the easiest task in
Mongoose now the other thing is that
models often times tend to grow out of
size with Mongoose so the more
validation you add to your models the
more they’re gonna grow and the more
difficult it’s gonna become to maintain
them now the last approach to validation
we’re gonna talk about in this video is
going to be object schema validation so
there’s actually quite a few libraries
for validation of objects one of the
most popular wants is a JV this one is
actually optimized for performance so in
fact as they claim that’s actually one
of the fastest libraries for validating
JSON objects it has a tremendous number
of downloads per week but the only
downside is that it’s actually pretty
boilerplate heavy so if you actually
want to do validation on your objects
you have to be ready to write out quite
a complicated and cumbersome boilerplate
to perform that validation so it’s more
of a low-level library like I said but a
more expressive library would be
something like scheme that’s come out
recently and it actually has the same
validation pattern as Mongoose so you
could define the same types of objects
so things like string and number regex
you could also pass in custom validators
so you can provide a custom logic to
validate those fields of course you
could also customize the error messages
I would have used this library and in
fact it has a very similar API to
Mongoose it seems to be quite compatible
with it but the only downside is that
this library is not very popular yet so
most often you would probably see a
library like joy used on the server side
with nodejs
joy is a very very popular library for
validation it’s currently at version 14
and it’s been evolving for quite a few
years now and the thing that I really
liked is that it has a very expressive
API so for example to validate an object
that has let’s say username password
access token so you could imagine that
this could be let’s say a user object
you want to validate that object you
could create
EEMA for it so you call enjoy object you
can pass in the list of keys for that
object and then you can start validating
them so for example you could validate a
string you could tell it to be
alphanumeric you could also set the
minimum length the maximum length you
could specify the field to be required
you could also use the utility email
method and of course you have access to
things like regex and a lot of other
things as well so it’s a very useful
library service side now for browsers is
also a library called yup this one is
actually used in for Mac but because
we’re gonna be doing validation
primarily on the server side we’re gonna
be using joy so joy is very expressive
and readable like I said it has a very
understandable and straightforward API
it’s also very dry and it doesn’t really
clutter up as much as we solvers or util
methods do and of course it’s also
extensible and customizable so you could
of course create your custom validators
you could also create plugins then of
course you can define custom error
messages as well now one of the
downsides is that the default messages
and joy are actually pretty cryptic but
like I said there’s still customizable
so there’s ways around that so what
we’re gonna do for this tutorial is
we’re gonna use joy for validation so
let me go back to my terminal what I’m
gonna do is I’m gonna do a yarn add a
joy so let’s add it as a dependency
let’s go back in here and in fact since
we are looking at the user model and
when I make a small correction to the
previous video you may or may not know
that Mongoose has been an evolving
library in fact it’s been around even
before promises have been introduced in
no js’ and definitely way before the
async/await syntax has been added to the
spec of echo script now because of that
previously mongoose mostly worked with
callbacks and that’s because we have to
pass in an arguments for the next
callback and we also had to manually
call it from our code but as it turns
out that we’re using the async/await
syntax we can actually get rid of the
next go back we don’t actually need to
call it as it turns out so we can
basically just remove that try catch and
I’m also going to remove the callback
from the function itself now as far as
having a try/catch this function can of
course fail and it could fail for
example if you pass in an undefined
instead of an actual string but this is
what validation is going to be used for
so this is gonna be the next step for us to look into

Please follow and like us: