Press "Enter" to skip to content

MERN Stack & GraphQL – #8 Validation with joi and Mongoose


so now once joy is installed we can
actually go ahead and create an object
schema so I’m gonna create a new folder
we’re gonna call it schemas now this one
is not gonna refer to graph QL schemas
this is gonna refer to objects schemas
for joy so we can create a definition
for user let’s go ahead and import joy
from joy we’re gonna export the faults
and object schema definition so it can
actually call joy object once we call
that method we can provide a set of keys
to the keys method so actually let’s go
back to our model what I’m gonna do is
I’m gonna copy the email address
username name and password let’s go
ahead and put them in here now the email
address is going to be a string so we
can go ahead and call joy dot string
this one is going to be an email address
so we can call the helper email method
we can also tell joy that this field has
to be required and lastly we can also
provide a special label property because
by default it’s going to be the key
itself it’s going to be lower case email
in this case we want it to be
capitalized so let’s do email like this
now for the username this one is also
going to be a string in fact just like
all of them so what’s called a string
method we’re gonna make sure that the
user name is alphanumeric so we can call
the elf enum method we could tell it to
be at least four characters in length or
maximum of let’s say 30 characters once
again we can also make it required so
let’s call of the required method and
we’re also going to assign a label just
like we did with the email address in
this case it’s going to be user name so
let’s do exactly that now for the name
this one is gonna be a string so let’s
do joy string we could tell it to be a
maximum of 254 for example required and
then for the label let’s say we’re gonna
have a name like this and this one could
be a full name so first name and last
name now for the password let’s do a joy
string not strict but string and in fact
what we can do here is we can pass in a
regex expression now for something like
this we could switch back to google
chrome let’s do a quick search for reg
axe for password let’s look for
something on Stack Overflow and we can
go ahead and open the first one so if we
look at some of the answers here
scroll down you’re gonna find an answer
to check for a password that has at
least a lowercase letter an uppercase
letter at digit as well as one special
character so we could go ahead and copy
that expression let me go back to my
editor and just to have something
working very quickly we’re gonna paste
in that same expression in this case the
only modification I’m gonna do is I’m
gonna change this parameter over here so
we’re gonna check for a length between 8
and 30 characters like this and then the
label field I’m gonna call it password
like this so once we export this object
let’s also go ahead and create a barrel
file so it’s gonna be an index J s so
let’s do an export default let’s say
sign up because this is going to refer
to this sign up mutation from user like
this now going back to our resolvers
what we can do in those cases we can
actually go ahead and perform validation
instead of the sign up mutation so let’s
first of all import joy and poor joy
from joy like this we’re gonna do is
we’re gonna call joy that validate
method well this one is going to expect
that the first argument is going to be
the input so the actual object we want
to validate and the second thing is
going to be the validation schema so
we’re going to pass in the signup schema
that we’ve just created so let’s
actually go ahead and import it I’m
gonna do import sign up from you have to
go one level up schemas like this and
this method in fact is asynchronous so
we can actually go ahead and call a wait
on it and because of that I’m gonna also
add a sync keyword in front of the
function like this so let’s actually go
back to our terminal I’m gonna do a yarn
dev to start the server so we’re gonna
try to fire off a mutation
so let’s write up a sign of mutation
we’re gonna pass in an email address
let’s try and pass an empty email if we
try to ask for an ID of course this is
gonna fail but it’s not going to fail
because of joy it’s actually going to
fail because of Bilton gravity all
validation in this case we’re missing a
bunch of arguments so we’re gonna pass
in the username let’s do an empty string
let’s try a name same thing and also
password like this now let’s see this is
gonna pass the validation and of course
it feels now we get only one error in
this case even though a lot of things
are failing so for example the username
cannot be empty the same thing applies
to name the same thing also applies to
the password but in this case we’re only
getting one error message and that’s
because by default joy stops validating
after the first validation failure but
we could actually customize this
behavior by passing a special abort
early flag and we get said this once you
false like this so if I save it and if I
try to rerun the query once again we’re
gonna get a bunch of validation errors
now in this case you can see that the
regex for the password
actually gets dumped out what we can do
in this case is we can actually go ahead
and customize the error message so going
back to the user schema what we can do
is we can call the options method we
could pass in the language key in this
case we’re targeting this string method
and then we’re gonna target regex
after it now the one thing that’s
special about the regex object is that
we also have to pass any special base
property but once we do we can actually
specify a custom message so for instance
password must have at least one a
lowercase letter one uppercase letter
one digit and one special character like
this so if I fire off this same query
once again you can see that the error
has been customized like this so at
least we’re not exposing the regex to
the client so let’s try providing a
valid input so i’m gonna do alex at
gmail.com
the username is going to be alex 996 for
example for the name we’re gonna set
alex and then the password we’ll go try
secret 12 exclamation points so we’ll
try saving that it seems like the user
has been saved now to double check you
could either use the unlap user
interface but what i’m actually going to
do is i’m gonna open up a new terminal
session from then on i’m gonna try to
connect to my buting the Mogul client
which you could actually install
separately prefer your own help we can
actually see a bunch of options that are
available to us so things like host or
port I’m going to try connecting to the
chat database I’m gonna pass any host
as well as the port in this case we’re
connecting as the admin user and also
provide the password separately and then
the last argument would be the
authentication database so in this case
we were indicating against each database
so let’s try running it I’m gonna
provide my password and it seems like we
are now connected so let me try DB we
can act it to the chat database I’m
gonna do show collections and we get the
users collection so in fact from now we
could do DB users dot find let’s see if
we have anything in there it also do
pretty on it like this and as you can
see we’re getting one user and in fact
this is the user that we’ve just created
the only problem right now is we’re not
validating for uniqueness of fields so
for example we already establish that
the email has to be unique and the same
also applies to the username so in fact
if I try to rerun the same query it’s
going to succeed and it’s actually going
to create a new user instance now that’s
a problem so we’re gonna try to work
around that with Mongoose validation so
inside of a model would save for an
email field we can actually pass in an
object and we can also specify the type
so this is gonna be a string and just
like before but if we switch back to the
validation section Mongoose actually
allows you to define custom validation
logic so let me look for custom
evaluators so as you can see you can
define your field that you can pass in
the validate option with an object we
can have a validator as well as a
message so try to do exactly that and
back in our editor
we’re gonna have a validate property
with an object in this case the
validator could be a function so we
could either use a traditional function
if we care about the this keyword if we
need to have access to it but in this
case it’s actually going to be the
document and not the query so we can’t
go ahead and try to do a query inside of
this function but what can do instead is
we can actually pass in a closure what
I’m going to do is I’m going to pass in
an arrow function this arrow function is
actually going to accept an email
address like this now what this function
is going to do is it’s gonna try to
perform a query on the user model but
because the user model has not been
created yet we’re actually going to pass
in a closure function what I’m going to
do is I’m going to separate the last
statement we’re gonna do an export
default of user
and then the user is going to become a
constant over here so this one is going
to refer to the actual user model
because this function for a validator is
going to be a closure we could actually
go ahead and have access to the user
model like this so we can call the wear
method in this case what we’re trying to
do is we’re trying to check if the email
field is actually unique so it hasn’t
been duplicated yet so we’re supplying
the email property this one is going to
of course expand to email call and email
and then we could try to do a count on
it so we could do a count on that query
in fact count is gonna give us a
deprecation warnings so instead of count
we could do count documents like this
and then at the end of it we can also
check if that count equals zero so the
premise for this validation is to check
whether the count of users with this
email ID in fact equals zero so we’re
basically checking if the user with a
given email address exists now if they
don’t exist then the validation is going
to pass but if the email already exists
in the database that means that the
email has already been taken so we can’t
reuse it again now in this case because
this operation of counting the documents
is asynchronous we also need to make
sure we pass in the await keyword so in
this case we can make the whole function
asynchronous like this now the second
argument of course would be the message
now looking back at the docs in fact
what we could do is we can pass in a
message as a function it’s going to
receive an object with the value
provided by the user so let’s do exactly
that we’re gonna extract out the value
from that object we’re going to pass
back a string with backticks we could
say email we could inject the email
inside of it and say email has already
been taken like this and in this case we
could either call value and email like
this or we could basically just use the
value directly now the injection of this
value of course has security
implications especially for XSS attacks
so when I go ahead and leave a to do
note for security to come back and fix
it later so once we have this check in
place let’s try to go back to our
playground so if I try to execute this
quarry once again we’re going
get an error message because the email
has already been taken but if I try a
different one let’s say max at gmail.com
if I run it as you can see the user gets
created
now once to get it to recap this what
we’re doing is we’re creating a custom
validator on the Mongoose model we are
performing a count of documents using
the given email address now if that
email address matches an existing user
document in the users collection then
that check is going to fail because the
account is going to be one and that’s
why we’re gonna get a validation error
message but if the check is false and
the validation is going to succeed and
we’re going to insert the record to the
database so if we go back to the
terminal we can in fact check to make
sure that the object has been created
now we probably also need to have the
same check for the user name in fact we
could just basically copy it so let’s
put on the object in this case we’re
gonna be checking by user name instead
of email and of course we could say for
the error message user name has already
been taken as you can see doing the
validation with the account is
repetitive so what we can do is we can
instead create a static method on the
model so we can go ahead and scroll down
a bit into user schema and let’s access
the statics property when I create a
special method let’s call it don’t exist
in fact this one is inspired by the same
method in the eloquent or M in level but
this one could basically be a
traditional function so what we can do
is we can expect a set of options to
that function and we can return this dot
where so we could pass any options
inside and afterwards we could basically
to a count of documents on that query
now in this case once again we’re using
a traditional function that’s because we
need to have access to that this
keyboard and once again we’re gonna put
in a weight in front of it and we’re
gonna do a sync on the function so now
instead of doing a count twice what we
could do is we could basically call user
dot doesn’t exist as a static method and
we can go ahead and pass any set of
options with the user name inside of it
or for the email address we can also
pass in the email of course
so let’s do exactly that and I pass any
email in this case the functions don’t
need to be asynchronous because we
already have async await in the method
itself in here so once that is done
let’s go back to our browser I’m gonna
try to insert the same record with the
same email address and we get two error
messages because both the email address
and the user name has been already taken
but if I do let’s say Joe at gmail.com
and if I run that we’re gonna get an
error because the username has been
taken and once again let’s write Alex
we’re gonna change from Alex 996 to Alex
12 in this case the email has been taken
and that’s why we get that error and
once again of course we get the same
validation as we had before with joy so
let’s say if I try passing an empty
shreya for a name of course this one is
going to fail but it’s going to first
fail at the joy level and not at the
Mongoose level so let’s pass Alex I’m
gonna try Alex 10 and Gmail comm let’s
do that same username so let’s try
inserting it and as you can see the user
gets created so once again to recap
we’re using a custom doesn’t exist
method that we define as a static method
that’s what we have to access the
statics property we’re doing a count of
documents based on the provided options
in the case of the email it’s the email
address and with the user name it’s
actually the user name field so that’s
basically it for validation in graph QL
now as I said there’s different
approaches for doing validation once
again nothing stops you from basically
combining these different approaches so
as you saw we used joy for object schema
validation
it’s very expressive and readable and in
fact it’s very pleasurable to work with
but of course you could combine it with
something like Mongoose validation which
comes out of the box and with longest
validation and might make more sense to
put in data-driven validations so for
example doing queries instead of your
custom validators so something like that
might as well be defined on the Mongoose
model itself so that’s pretty much it
for this video hope you found it useful
in the next one we’re gonna have a deep
dive into authentication so I hope you
stick around for that and we’re gonna see you next time take care

Please follow and like us: