AdonisJS vs NestJS | NodeJS API Frameworks

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you're splitting up your energy in all different directions like here on the left then naturally you just don't really progress as fast as if you just focused on one direction right and this is very much if i change the label here the left side would be adonis and the right side would be nest [Music] hey guys welcome back to channel in this video we're going to compare adonis js with nesjs these are two frameworks that i believe are sort of competing for kind of the same space both of them are very opinionated they have a very specific way of doing things you know i think they both believe that something like express is too minimal and does not have enough structure or specifically does not have enough architecture that these frameworks try to kind of fix that and they also bring in their own uh integrations their own ideas so let's talk about it so what i think i'm gonna do in this video is i am going to create the same rest api you know crud create read update delete and then as i'm doing that i'm going to compare and contrast the experience between adonis and sjs and before we get started i do have to clarify some things up front um if you've seen enough of the content from my channel you'll know that i have a very strong bias towards an sjs already so coming into it i already like nest js so you have to understand that i have i already have that bias second in the adonis side we're taking a look at adonis js version 5 which as you can see is currently in preview um the reason for that is i think that adonis 5 represents what the author ultimately wants to get to right that's it represents the vision of what he thinks this framework should be now it's not it's not completely ready right it's not complete it's not it's not generally available it is in preview you know basically alpha so just keep that in mind there's going to be obvious parts here where you know it's going to look like adonis obviously is not good enough yet but some of the ideas are definitely there there's stuff here that i really like there's stuff here that i don't like and i'm gonna cover that throughout the video all right so let's go ahead and get started like i said i'm gonna build the same api um on both sides and i'm gonna try and see you know i'm gonna try and compare and contrast i'm also gonna be referencing the documentation often throughout this video because i do think part of the things one of the main things you should evaluate when you're looking at these frameworks is the documentation if the documentation is not good it's not going to help you you're not going to know how to build stuff so it's important and on top of that it's also good to understand the different principles behind these frameworks you know even though they are sort of trying to solve the same problem they have a very different approach you know adonis is very much let's try to build uh everything from scratch well not everything but most things whereas sjs tries to reuse a lot of the things that already exist in the ecosystem so you'll see that throughout this video and we're going to see you know is that a good idea or is that a bad idea and we'll discuss all right so i think the first thing we're going to do here is we're going to compare the experience of creating a new project from scratch you know let's say you don't know anything about these frameworks how good is the documentation at explaining to you how to get started as well as the the code that it starts you off with how easy it is to to pick up so let's start with adonis all right so to get started with adonis i've got a terminal on the right side here documentation tells me that i should npm init adonis ts app and then the name of my project which i'm just going to call adonis api right away it starts asking me do i want an api server or a web application i'm going to go with an api server asks me the project name do i want eslint let's do yes prettier sure thing some quick impressions out of this i really like that right off the bat it asks you what do you want um there's some things that you can immediately configure so i really like that and uh the cli in general just looks very simple and clean i like that so it looks like it all still already installed my packages for me and it says that it's created you can go in and start running that so what i'm actually going to do is let's go ahead and change the directory to that and then i'm just going to open up vs code now what i'm actually going to do for this video is i'm going to use this vs code extension called peacock i'm going to use this to assign a color to my vs code window so that you guys will know every time i switch between the different code bases uh the blue one will be adonis and then later i'm going to make the nestjs codebase red you know just so you guys don't get confused when i'm switching around so in the documentation on the left it tells me that there's this command a serve watch to start the application i also noticed that in factory.json it does have that dev script which is the same thing so i'm just gonna run that actually and when i run that it tells me that the server is on localhost 3333 and it gives me a basic hello world let's take a look at what's generating that so this is actually coming from this routes.ts file under the start folder it just returns an object and we get back our hello world on the left all right so simple enough i think one thing that is probably worth talking about right off the bat here is notice that the the thing it creates you is this very specific folder structure this is i think where you're immediately going to see the difference between adonis and nest adonis has this very specific folder structure where it's divided by the different types of things so for example you know there is a providers folder where all the providers will be i do find that as a person who hasn't spent much time with adonis this seems to be a very heavy thing to start off with like all of a sudden there's all these folders that you have to know what what things do go goes in these folders so there's a little bit of an immediate learning curve but i assume once you play around with it a little bit it probably makes a ton of sense and as i click around here there's definitely a lot of files here that just doesn't yet make sense to me again due to my lack of experience but i assume it's probably something covered in the documentation um one thing that i've heard about adonis is that it's pulling a lot of inspiration from laravel as well as ruby on rails so i think if you're coming from that world maybe this stuff is actually very familiar to you i'm coming more from the expressjs world so it's not immediately familiar but again i'm sure it's something that's probably covered in the documentation so that's the basic experience of creating a new application you get back a a base set of folders and files some of them don't immediately make sense to me this route one looks pretty similar to a an express route right you've got a get a path and then some kind of callback and it returns an object and we'll go a little bit deeper more into routing in a second here but before we go too deep into adonis let's first switch over to sjs and kind of compare and contrast what's the um create an application experience like in that world all right so i got the sjs documentation on the right this time in the terminal on the left and it tells me that to get started you first need to globally install the nestjs cli and then you can use that to create a new project like so i already have that installed so i'm just going to go ahead and create that new project which i'm going to call nest api so kind of similar thing it generates a bunch of files for me it asks me in this case it's asking me do i want npm or yarn i'll pick npm that's something that seemingly was that a question on adonis i guess it prefers npm in this case and sjs didn't really ask me anything else it just said here's your application you can go into it so i'm going to do the same exact thing i'm going to open up a new vs code editor and this time like i said i'm going to color this to be red so that you know the red terminal is all right so let's take a look at our dev script in here similarly they have a start dev it turns on the application and goes into watch mode so that every time you change your code it'll refresh and i know that this is running in localhost 3000 it is one thing that i wish they said in the terminal by default which they don't i just know from experience it's on 3000 but you know pretty basic there's a lot less folders in this case compared to adonis you just got your basic src folder right a lot more simple i think to get started with you have a main.ts which is kind of just your basic entry point and then you got a module which has a controller and a provider and when you look into that controller that's where you'll see that the hello is coming from this controller alright so there's not really much else to talk about on the nest side other than you know that's pretty simple minimal files and there's not much more to really explore it's pretty simple if you go into the documentation for controllers it does immediately start talking about how you can change the the string that you provide in these decorators to start manipulating the uh the end points that these controller starts hooking onto right so right off the bat kind of just comparing them side by side right again lots of immediate files and configuration on the adonis side whereas sjs starts out fairly minimal but effectively outputs you the same application right as a basic hello world application all right so let's go ahead and do a sort of side-by-side comparison between uh controllers and nest and controllers in adonis now the initial code that comes out with adonis doesn't actually have controllers in it but if you read the documentation a little bit it does go into saying that the routes file is actually not really meant to have that much logic in it in fact it's supposed to just have the route definitions and then everything else is forwarded to the controller files which why don't we go ahead and kind of build something on both sides so that you guys can see what i'm what i'm talking about here so in the adonis documentation it tells me that uh they have a cli command for generating new controllers so let's imagine that we're trying to create user crud right i want to be able to create read update delete users so with that goal in mind let's go ahead and make our user controller so it says i gotta do node ace controller user and by the way ace here is kind of um adonis's own cli as you can see that it automatically kind of comes with the application that's a little bit different from nsjs where it tells you to install the cli globally so that's one that's one small thing that i don't know anyone cares about but there it is it does tell me that the controller is created in this path so i'm gonna go there and it just starts out with an empty class so i've got my controller but to actually have things route to this controller we actually have to go back into our routes file and then we have to define some things here that would say that effectively say for these types of endpoints or requests it needs to go into our user's controller so one thing to quickly note here is on the routing that's usually where you would define your http method so you can see for a typical crud on the left side here it looks something like this which i'm actually going to go ahead and copy that just so you guys can see a little bit better and i'm gonna go ahead and paste it over here on the right side um and right off the bat this is actually something that i noticed when i first i was first looking into this was that you guys see the red squiggly here those are actually typos in the documentation these should all have async in it so that's a little bit disappointing but i kind of give the author a little bit of a benefit of the doubt because this thing is in preview so you know you can't expect it to be perfect um so this is kind of how you would create you know your basic crud routing this is probably similar to if you've used express before it looks very much like express now i'm actually going to change these to be users because that's the thing where we're trying to create here right we're trying to create users crud right so if you're trying to make basic crud routing that's that's what it would look like and it's easy to think that maybe you should be doing the logic here right but the the way it actually explains it to you in the documentation is that the the routing file is meant to be as clean as possible it's meant to just route to controllers it's not necessarily meant to really do much more logic beyond that it can i don't think it prevents you but that's not the intention so for example you'll see in the the left side here for the way that we route routes to the controller is for example i want to be able to do i want to be able to route get users to my user's controller and you just do it like this instead of providing a callback you provide a string and this string represents a method inside the user's controller so for example if it says usercontrol.index that means i need to define a you know an index method kind of like here copy this that's how kind that's kind of how the mapping works is based on the string that you provide it's going to map to the method in the user's controller so why don't we real quick run this and kind of just see it in action so in a terminal i have off screen i did start the the application again which runs the application in local 333 and if i go to slash users what i expect to get back is this array that i have so from here you can kind of imagine that to do the rest of the crud i just have to do a bunch of these kind of definitions so for example if i want to do user slash id i might do something like let's change this to a user controller get by let's call it get one it's called find one and in my user controller i need to define a find one method which let's just say returning let's just return something with id one for now for simplicity so i expect if i go to slash user slash one and later we're gonna look into how to parse this id from the url dynamically but let's just hit enter there and i get back that user with id1 something that i immediately like about this is that you can see we're just returning you know eraser objects directly right so there's no if you're coming from express there's no doing like rest.json or something like that it automatically tries to figure out all right what is the what is the response content type right you can also return strings and it immediately figures that out for you so it it simplifies it really nicely and this is also something that happens in nest so it's not completely specific to adonis one thing though that i'm not sure i'm a huge fan of is this whole thing with the strings if you read the documentation and if i understand it correctly the idea is that they're trying to make sure that one the route kind of turns into something that you can just easily scan so for example let me do some cleanup here real quick so the idea is that you can go into this routes file and at a high level you can immediately see what are all my routes and then where which controllers does it map to um which is kind of nice i kind of like being able to have that high level view of like what are all the routes in my application and we'll compare this with nes in a second here and just thinking out loud one thing though that i don't like is that because we're using strings instead of actually taking advantage of you know typescript types in this case i can very easily make a mistake here like if i made this like you know gibberish let's just say i misspelled it or maybe i'd added an extra e here um it doesn't tell me that that's wrong it's not able to look into this controller and say that doesn't exist right and some of these i haven't defined yet and it doesn't tell me one thing that i did notice is if i let's say let's add an extra x here so that's gonna make it break but again it doesn't immediately tell me it doesn't tell me in the terminal at all but if i go to slash users then it does tell me it'll say missing method index on user controller so the problem there to me is that it's not going to inform me that there's a problem until runtime which i feel like if you're trying to use typescript right isn't that the whole point for typescript to kind of help prevent some of those bugs for you upfront i wonder if maybe if i run build does it tell me when i start building for production that i'm missing methods and it doesn't seem to it built successfully so i don't know that that's a design decision that i'm not sure i agree with i do understand the the benefits to it i think in the documentation it says that because the mapping here is done with strings it actually is able to lazy load the controllers in versus something like nest where it has to load everything up before the application starts up so you do notice this when you start adonis it boots up very quickly compared to nest but you lose some of that typing ability so i don't know if really that's a good benefit it's more of a stylistic preference if you ask me so while this is like really nice and clean i'm not sure that it's really that good of a benefit of yeah you you lazy load but you potentially easily introduce runtime bugs into your application i don't know how i feel about that there is also a shortcut to because this is probably a typical pattern that you do with crud so according to the documentation you can also do route that resource um and let me do that and i'll show you what happens so if i switch this to route that resource looks like just users without the slash and then we just change this to users controller it looks like what that does is it uh uses conventions to automatically map it to specific methods so if i do node ace list routes so it tells me that it auto generated all of this stuff well it didn't auto generate it for me right i still need to define it myself on the controller but there is a convention here that uh slash users will automatically go to index and slash user id will automatically go to userscontroller.show so i should actually rename this to show and just to do a quick test let's go ahead and refresh on slash users that still brings me back my array here and if i do slash one i get back this object so that is kind of nice i like that kind of hey here's a convention follow it you know and if everyone's following it you kind of end up having consistent code so that's kind of nice what one feedback i would have for the author is you you got to build something in here that checks for um maybe missing implementation right like if you meant to do resource but you only implemented two of the methods then you know what happens all right so that's 101 how to do basic crud routing and adonis i do think it's super clean um but i feel like there's still room for improvement uh why don't we go ahead and create the same exact thing in sjs and kind of compare them side by side all right so i'm back in my next js app and from experience i'm actually aware that they also have sort of a cli command that helps generate an entire crud resource so i can just go in the terminal and do nest generate resource users and i hit enter here and it's going to give me a couple different options i'm going to select rest api it's going to say do you want crud entry points yes that's exactly what i want and then it starts installing some it starts creating files for me and then it also installed some stuff so let's take a look at what got generated in nessus case it made me a users folder which within that there is immediately a file for dtos which represent the shape of data that's being transferred around there is immediately a user entity file which is just an empty user class representation um in a little bit here we'll compare how do you how what's the difference like between nest and adonis when it comes to representing a database table so we'll explore that a little bit more and then it specifically creates a new users module which registers a user's controller and a user service within the users controller it has basically all it has a class that has all of the methods for me built right away it's got it's got the post it's got the get get by id and patch for updating and then delete right there's all micro endpoints and it's automatically kind of forwarding to the user service which is where our implementation later on in this video will live of all right what's the underlying logic do you talk to a database do you go somewhere else whatever and if i run this and go to the browser kind of similar thing if i go to slash users you know i get back a base you know mock implementation this action returns all users and if i do slash one this action returns number one so nice thing with this is immediately has an example of how to parse out the the parameter um so we'll explore to we'll explore in the adonis side how to do that in a second here um but right off the bat i think there's some interesting things to kind of compare between the two implementations of the controller we've got our adonis controller on the left we got our nest users controller on the right right these are doing the same exact thing pretty much and i tried to reorganize a little bit so that they kind of match one to one right so you have your your create here on the left you gotta create here on the right we got find all we got index we got show and we got find one we've got update and we got update and then we got delete we got destroy and we got remove right so your crud on both sides and let's go ahead and talk a little bit about you know what's different what i like and dislike uh and i did implement some of these other methods uh which you didn't see just so that we can have a kind of a fair comparison here i also added these uh i this structure the params which is how you get back to id so fyi at a high level there really are basically the same thing the difference is that again adonis says that your route definitions should live on this file it should be you know as clean as possible and your controllers is just in charge of the logic it doesn't have any information about the routing itself it doesn't have http verbs whereas in sjs it does say that the controller is in charge of being kind of the fusion between routing and what people often call as controller but they're kind of in the same idea if you think about it where in sas you also don't necessarily want a ton of logic to live in the controller itself you actually want to delegate to the service as you can see here the generated code does exactly that so a lot of the a lot of the code that the core business logic in your code ends up living in the services and you'll also notice later is this is where we're going to define our database queries whereas in adonis a lot of that logic oftentimes lives in the controller and there is one problem to that which is what about reusable logic how do you reuse controllers and the documentation does say that you're not supposed to reuse controllers if you have that sort of need to reuse logic you should actually move it to a service object you know it says it's there's a section here about using controllers and it says if you want to reuse move that stuff to its own service object so it pretty much has the same opinion as nest um it just by default doesn't enforce it i guess whereas nest right away says hey move your logic your your business logic into services immediately that's where they have to live now one thing to maybe immediately notice between the two is in sjs you'll very easily uh start noticing dependency injection and where you're kind of seeing these uh constructor definitions and it has stuff in it that automatically get injected you know for example nest is behind the scenes kind of creating an instance of user service and automatically injecting into this user controller via the constructor the constructor and some of the base ideas behind that is actually also in adonis because if you take a look around you'll notice that they're import statements some of them have ioc in it which stands for inversion of control so a lot of these imports that you'll see around the the application in adonis is actually doing kind of the same fundamentals behind dependency injection right dependency injection is kind of a form of inversion of control but the way that adonis kind of deals with it is kind of it magically does it for you through this import strategy which is actually really cool because it's sort of like the same as saying what if i can just import user service like this on the right but automatically it the framework takes care of providing me with the instance i don't need to define a constructor like this where it gets injected so it kind of magically you know makes it look like you're just doing basic imports but it's actually doing inversion of control for you behind the scenes so that's something really cool that i like about adonis the thing that's not super clear however is what's the what's the testing strategy right so usually when i see inversion of control or dependency injection in frameworks like this is that the the big idea is that it makes testing easy because in tests i can just for example on the right here i can just create a different you know mock instance of the user service during testing and this is actually something that is very well covered in the documentation itself there is a section here under fundamentals testing you know you can read through this if you'd like but it goes into how do you go about kind of utilizing the fact that they're using dependency injection and how do you override implementations in a test right so you can kind of see examples of override provider now now when i take a look at the adonis side of things uh something that was super surprising to me is there's no documentation yet on testing right and i feel like that's the biggest thing with you know a version of control is to kind of help you with testing so i would have expected that one of the primary things that should already be in the documentation is testing so that's a little bit disappointing uh that it's not already here if i do a search for testing there is a blog post about it on how to test set up tests in adonis which basically just goes into the author saying i don't like jest so i'm gonna use something else um which again i don't know that i would agree to because jess is pretty amazing so they're using joppa but they don't really go into detail about how do you how do you mock things how do you fake how do you provide fake implementations for dependencies so again maybe it's here somewhere and i just missed it but uh something that i wish was already here to begin with if you do go into the adonis v4 documentation there's some stuff there about testing and how to do exactly what i'm looking for but again what we're working with here is in preview so i guess i feel like you could use that as an excuse for a lot of this stuff but i don't know i feel like that should be number one if you want to be taken seriously you know you should have some some solid testing documentation right if we go back to nest js like look at the amount of detail that they have in here about testing um you have to have that in my opinion right off the bat anyways i do again think that this kind of ever inversion of control that just looks like a regular import that's something that is i've never seen that in any javascript framework so that's really cool to see i would love to see something like that in in sjs but again some documentation on how do you go about sort of providing mock implementations for that that would be great to add to the documentation now if it was using jest for the framework if you know jess is really good at mocking imports imported modules but his blog post just pointed out that he doesn't like jest so how do you i don't know maybe i missed that detail if you know how to do this stuff in the comments you know maybe let me know but i do like the approach that it's trying to take where you can see that the files are meant to be kind of very lightweight you know it's just a basic javascript class it's got none of these you know decorators decorators are very heavy and sjs some people like that stuff some people don't even things like you can see just getting just parsing the id out of the url you have to go through a decorator um same thing with getting the body out in adonis you kind of just get it out of the request object like this and actually just to kind of make this a more complete example let's let's go ahead and dive into parsing the request body on the adonis side because there are some some interesting stuff to see there as well as validation so let's go ahead and take a look all right so we're on back on the adonis v5 documentation and there's a section here about form submissions it talks about kind of its own body parser which is used both for um you know parsing and validating but you'll see that it has support for a lot of things including you know your base json but as well as stuff like you know multi-part in the express nest world usually you would have to use something else for multi-part like you some of you might have heard of malta people usually use that for you know file uploads and stuff but if you scroll down a little bit here it says that all right there is it's a middleware that you have to have and it that is in the file already and after you have that registered you have access to these uh request methods so you can see that the request is something you can destructure out of the uh the object that you get in the method so let's take a look at how to do that so in the code say that we were trying to create a user um looks like we get back here in an object of some sort that has on this type up here http context contract and within that i've got my request so to get the body you can do something like request dot all right and to kind of just test this out why don't we go ahead and just return the body back right so whatever we push into it it's going to return back to us so i'm going to open a insomnia on the right here it's it's kind of like postman if you've ever used that it's just an alternative so let's say that we're trying to do a post on slash users and i'm gonna have a json here that has named test right pretty basic so i expect that i'm gonna get this object back when i hit send here uh this is actually telling me that this method should be store not create so let me update that and let's hit send again and now i get back my test real quick i had to go back to documentation here to see why did i think that was create and that's because there's actually two extra routes when you do route that resource which is meant for a web application like if you have a a forum page where you can do the create that's what where they do slash post create will serve up that page now i don't actually want that so i'm supposed to add this dot api only to my route config so that doesn't have that right so we're just making a crud api and that's it all right so as you saw in the insomnia test that's how we're able to get the uh the body out of the request and then the documentation also tells me that uh you can do validation again something that adonis the adonis author seemingly created is their own schema validator and this is a common theme that you'll see in the adana space is that they very often kind of take basically they reinvent the wheel a lot as is the easiest way i can explain it right because schema validation is not new there's a lot of stuff that already does this type of work like joy class validator um but in adonis the author very much believes that it's sort of like spacex if you guys are familiar where spacex instead of trying to buy a bunch of expensive rocket parts from different manufacturers they instead decided to just build parts themselves which keeps the cost down but also improves you know the integration between their parts so that's kind of the same thing that the adonis author is trying to do at least that's how i i kind of perceive it is that they're trying to increase the uh they're trying to enhance the integration between these different parts and they've the author i think has found that instead of trying to integrate with existing libraries he finds that it's sometimes easier to just make his own which um i don't know if i if i would agree with that strategy i think that if maybe he has a team behind this that would make a ton of sense but he's he's one guy i think pretty much um and he's trying to build all of this stuff so i don't know anyways before we get into a super deep rant there let's let's do some of this validating stuff so it says in the documentation on the right there that i can create a schema let's call this user schema and you can do schema.create and let's just say that i want to have a name in there a required name and that is a type of string okay you can also validate the request off of that so let's do kind of the same thing they're doing there await request dot validate schema provide our user schema and then something about cash key on the url all right so as you can see i don't know a ton about this i'm kind of just following the documentation um so you know you can kind of assume that the experience that i'm having is going to be similar to your experience if you're if you're new to it so maybe that's useful to you i don't know um and then i think what i'm going to do here is i'm going to return the the validated data and let's just kind of see how this works so if i go back into insomnia if i do a post um let's say that i just you know sent the wrong object how does it respond you'll notice that it gives back a http 422 with an errors object and it tells me that hey name is required so that's pretty cool it seemed pretty easy let's go back to name test and that should give me back my object so that's you can see validation working um one kind of random thing for me to nitpick here is you'll see that the documentation you know in ideal word this is something that can easily be copied and pasted right but you'll notice that there's actually an unused import here that i'm not sure why it's there it actually breaks the build if you run the build it's going to say you've got an unused import why is it here so i don't know that to me just seems like a copy and paste error in the documentation again i feel like this is in preview so you can kind of excuse it but you know documentation in my opinion is key if it's bad it's i don't know one thing that is cool here is this data that comes back actually has type definitions attached to it so you can see that it knows that this has a name in it because the schema provides that so that's kind of cool what's weird to me however is that the request body has zero type definite it has a generic object type of key and value but that basically makes it useless unless you make it go through the validator so i don't know i guess you just have to always validate your body in adonis which i would imagine is good practice anyways alright so that's how you do [Music] sending form data to a user's controller in adonis let's take a quick peek at sjs on how to do that on that side so i've got my nest code up again here uh and also the the documentation on the left side nest does have very solid documentation for uh validation and they do validation through what they call pipes and uh explain pipes simply pipes are basically middlewares except they're specifically meant for either validating or transforming data so you just think of them as like a special middleware that has a very specific job and it does have a couple built-in pipes that you can readily use so one of those is the validation pipe which we're going to add to our application so there's a section in the documentation here under auto validation it just tells me that i need to register the validation pipe globally so i'm going to do that i'm going to go into main.ts and i'm going to add that piece of code right here and you make sure to import it and then when i run the application i know this is actually gonna tell me i'm missing something but i wanted to show you real quick it's gonna say you need class validator you need that package so in another terminal i'm actually gonna do npm install class validator and i know from experience that it also needs class transformer so i'm gonna install those two things next in nest js you might have noticed earlier that we have this concept of dtos dtos are basically you can think of them as just classes that represent the shape of the data that's being transferred around so in our specific example here the request body in the adonis side we said that we want to provide a name which is a string so this create user dto just represents the shape of that request body but the thing that we can add here now is you can do something like with class validator you can do for example maybe you want to do is um alpha we only want letters in the name we don't want numbers you can see that this great user dto is being used in my post method here right the request body has a type of create user dto so just from that annotation let's go ahead and run this and kind of see it working in action i'm going to go ahead and open insomnia again and let me create a new request for nest api and i want to make that a post right and this one is coming from and this one is coming from localhost 3000 slash users and we're going to do the same exact thing we're going to provide a json body that has a name in it and maybe let's provide numbers because we know that's gonna make our validation fail if we hit send here kind of similar experience right we get back a status 400 um you might have noticed it earlier in adonis they sent fort 422 i think which is a bit more specific i think for this use case but you know 400 is not a bad status code to send but it since it has that same kind of experience where it sends you a list of here are your failing validations right and if i set this up to be just letters and hit send i get back my expected response right so that was pretty simple right if we again do a side by side comparison uh the big difference right is that in adonis the request body is not typed at all but you kind of feed it through the validator and the schema and that kind of enhances it to have a type whereas in sjs it immediately kind of takes the request body and kind of transform it into an instance of this class that automatically you know has these properties and it runs class validator through these decorators to validate your your class properties right so very different approaches i didn't dig too much into the what else i can do here in terms of changing the the schema right in class validator i think there's also a is string right so if you want to make it more of a one-to-one comparison but again very different approaches on one side you kind of define a schema and run your object through it whereas in sjs you define the shape as a class and you you decorate your your properties to have automatic validation if you ask me i really like the way that nest does this i feel like it's i don't know it's intuitive this is intuitive as well and adonis but it's pretty verbose um i don't know one one could also say that once you have a lot of decorators on this side on nest it also gets pretty verbose but um i'm not calling anything like validating or i'm not doing i'm not creating an instance of the class right that's all being done for me kind of automatically behind the scenes because i enabled that validation pipe it's basically kind of doing the same thing as well as uh you know it takes the request body it kind of transforms it and then it validates it but i don't have to see it it does it for me so i never have to do this sort of stuff plus the body is immediately has this type because of what we're doing here right so i as i pass around that dto it always has that shape i know what i know exactly what are the different properties that it has whereas here it seems like it you know request that all gives back a generic type which in my opinion is not as useful again you kind of have to feed it through the validator to kind of enhance it with types again different ways of doing the same thing i personally like uh the nest js way better all right so there's a couple you know there's quite a bit more stuff to kind of dig through and compare there's stuff that i don't think it's worth doing in code so i'm just gonna talk about it kind of comparing the documentation you know so for example uh we didn't really cover views and templating if you were trying to make a kind of server rendered ui you might want to look into this the gist of it is that basically adan has kind of created their own templating engine called edge again it's a recurring theme in this space is that the author likes to kind of reinvent things to a degree i get the motivation behind it but at the same time i feel like it's a little bit too much in sjs it does have the ability to kind of also emulate an nvc structure kind of like what adonis is trying to do but you can pick what templating engine you can use you know you can use handlebars you can because it's just using express by default and express works with all the different templating engines like pug ejs handlebars i think there's one called mustache right so it's not it's not trying to reinvent that space it's just kind of utilizing what already exists whereas adonis made their own um whether or not you kind of agree with that that's up for you to decide file uploads kind of similar thing you know adonis has their own way to because they're they have their own body parser that parser can also parse multi-part requests and the cool thing that i did notice here is that the same sort of schema validator that we use to validate a json body you can use that same schema validator to validate a a multi-part request with files right so you can see here they're validating an avatar to have a size of two megabytes and it has to be one of these extensions so that's pretty cool in sjs if you look at their file upload documentation um they kind of take the uh again utilize what's existing in the ecosystem which is multur so they have sort of a way for you to do again another decorator to get the file into your controller right you can kind of see you add this decorator and then it kind of just does its magic behind the scenes and feeds the file down into your controller and from there it's up to you what to do with that you know you can do your upload to s3 or something if you want there's also sessions documentation here where you'll notice in adonis right they have support for some initial very basic options you got your cookie you got your file and you got redis so they've got a pretty i would say it's kind of limited but maybe that's enough for you i don't know in again in the next js space because it's kind of think of it nest as a layer on top of express right so it means that everything almost everything that express has access to basically that entire express ecosystem which is huge nest very often also has access to it so express session is a very good express library that nes can just take take advantage of and when you look into the documentation of that you know take a look at what it supports in terms of storage right compatible session stores it's got all of these guys right you can do you can save it in databases you can save in dynamodb you can save it in redis basically anything you can think of is probably here right so wherever you want to store your sessions more likely than not it's already supported whereas in adonis you got these three basic options maybe that's good enough for you that's up to you to decide uh real quick uh let's also cover middleware you know i think middleware is a a thing that's you know pretty well known in in the node.js land because of express but basically middleware is sort of like logic that happens between you know the the request before the request gets to the controller itself you might want some logic to happen before it gets there so that's usually what middleware does and you could take a look in adonis you can define middlewares as classes and then you can just register them globally i think like this in the kernel that yes and on top of that you can also in the routing add middleware there where in after the route definition you can do dot middleware and then you define your your logic there this one is a little bit weird to me because like in my mind i think of middleware as like something in the middle right i would have i feel like it would be nice if the api first did route that middleware and then under it it had to get but i don't know if if that's possible i didn't play around with it but i don't know it's just weird to me to see that the middleware is at the bottom because technically this is gonna run first before it gets here right by definition of a middleware i don't know just a stylistic oddity to me um in sjs they also have the concept of middlewares and you can look into it kind of similar thing where you can kind of define a a middleware class and then you decide which routes does it apply to and you do the def you do the registering in the module right so for example in this example here they have a logger middleware that they want to assign to specifically the get cats route and what do i think about that i'm also not the biggest fan of this um i feel like it's not super intuitive to be defining the middleware in the module i feel like that should be maybe in the controller like where everything else is one thing that i do like about nest is they they kind of decided that there's certain types of middleware that has a specific enough job that we should just call it its own kind of type right so we talked about pipes a little bit earlier which are effectively middleware that does transform and validation there's also guards which are effectively middleware that specifically has the responsibility of authorization and authentication and then this just says if you've got any other generic stuff you know you can do you know the old-school generic middleware all right so that's a little bit about middlewares i don't think there's much really more for me to compare there so the gist of it is they can both do some form of middleware logic how you kind of define them and register them is just a little bit different but ultimately they can do the same thing exception handling between the two is i think fairly similar again kind of same idea of there's usually the idea of creating a class which represents a specific exception right so the adonis docs kind of walks you through uh creating a custom exception handler where you can see here they can do stuff like if the code is a certain type respond with this http value and you can also make these specific classes that you know has this definition of oh it has this status and this message and then you can manually throw it like this that's kind of similar thing with uh exceptions in in sj yes you kind of saw earlier with validation that it does do a little bit of the exception handling automatically for you right that's why we were able to uh get a status 400 back when the validation failed but it also provides you a way to kind of create your own you know custom http exceptions like this and then you know you there's also a bunch of these built in http exception classes right like 400 bad requests 404 not found and then you you know anytime you kind of respond where you want to respond with those specific http uh status codes you just throw the class you know so it's kind of similar thing they're doing on the left and right here and then they do have a way for you to kind of globally define you know what i i think of them as like catchers right they they capture a specific type of exception and then you can tell nest how to respond i think that's similar to what they're doing over here if i understand it correctly the the one where you do the handle so it's catching a specific type of error and you can tell it to respond a specific way so not really much to talk about here again basically they do the same thing a little bit of a different syntax but the underlying idea i think is the same another thing i wanted to talk about quickly is the different strategies for authentication on both sides in adonis again it kind of has the same underlying idea is that they kind of want to do authentication their own way so naturally that means that there's a limit to the different types of authentication that you can do so for example here i think they have documented how to go about you know looking up a user or a token in the database but they don't have any stuff here about you know things like social social logins right like log in with google or stuff like that i don't see anything here about that if you look at the adonis v4 documentation they do have some of that stuff so i imagine uh it's just too early for adonis v5 but again the the problem that i see here is that adonis again is trying to reinvent the wheel on a lot of places which ultimately slows it down as a whole right because he's the author is still trying to build all this other stuff so naturally he can't build everything he can only build a subset of what he thinks is the bare minimum that you should have now on the nest side it just simply has a really good integration with passport.js which has been around forever and as you might know passport has support for a wide range of authentication strategies right you've got your your socials like facebook google you've got uh oidc you've got jwt you've got you know login with password and username etc so basically ness kind of takes the stance of i'm not going to reinvent the wheel in this case there's this perfectly good library that you can use that supports all of these different types of authentication you know go at it and i have tried this and you know it works great um so i'm not gonna write the code to do the comparison on both sides i don't really think it's worth it because again they just have a completely different strategy that you know you just have to understand the limitations that you might have with adonis whereas nest kind of just opens up the world to you uh one section that i did want to kind of go back into the code to do some comparison is the database stuff so again reoccurring theme here with adonis is that they decided that none of the existing orms in the node space is good enough so they made their own uh called lucid so lucid is kind of their primary way to do data models i'll do a quick demo of this in a second and then they use connects for the more custom query builder as well as migrations so at least that's one place where i'm amazed they decided not to reinvent the wheel which is a good decision because nest is is a very good library for this stuff and then on the nest js side it by default is database agnostic it allows you to integrate with whatever database that you want so the big choice here i think is usually type orm for for sql databases you know as you can see it's the first one that's documented because i do think it's the one that has the best integration with nest so i'll probably do a comparison of maybe type or and lucid so again let's jump back into the code and let's see if we can do maybe a a connection to a sqlite database and let's see if we can do some queries off of that to kind of complete our user crud implementation so we've got our adonis code on the right again here and we've also have our we've got a code on the right and documentation on the left let's look into the setup instructions how do i go ahead and use sqlite so it tells me that in my terminal i need to install lucid so let's do that and then after installs there is a command from the cli node ace invoke at adonis js lucid and it gives me sort of an option to pick what is your database i'm going to select sqlite you hit space to check a box here or a circle and then hit enter and then it starts uh configuring stuff for me and it asked me where do you want to display uh instructions let's go ahead and select in the browser kind of want to see what this does and it opens up this page it just tells me i gotta provide my connection details via via environment variables but for sqlite i don't need that so i'm just going to close this and also notice that it created me these files if we took a look at config slash database got my sqlite connection in there and it's going to create this file that has db.colite in it and i believe it did also install sqlite for me right so it's in my dependencies here now before i can work with that database i need to create a user table so the way to do that is with migrations so there's a section here about schema migrations and it effectively just uses connects behind the scenes i believe it tells me that i need to do node ace make migration users and it creates me this file that has an up and down if you're familiar with migrations right that's usually something you'd see with an up and down and it looks like i have to define my schema in this file so if i were trying to do a user table with a name column looks like i just need to do something like that right so i expect that this is going to create me a database table with incrementing id which is a number and then uh a varchar name which i also want to make that not nullable because users gotta gotta gotta have a name and then it tells me that before i run my migrations i need to make sure to build so i'm actually just gonna run my build script here build successfully and now i'm gonna run my migration node ace run that says it ran so i assume that means i've got a user table in my sqlite database which is saved in a file so let's go ahead and look into how to query that so there is a query builder um which again i think just uses connects um so if you're someone who is very familiar with sql this is probably the the way you want to approach querying things so what adonis actually promotes that you use especially with lucid is the active record pattern and the gist of that is basically that you will have these files which represents your schema your database tables and then within these models uh you can also define custom methods if you want so why don't we just go ahead and kind of see an example of this so in the documentation it says that i can do node ace make model user i hit enter and it creates me that file and you just get back this base user model the one thing that is kind of odd is that it's it's not aware of what i already made in my migration now maybe this is my own bias from typo rm but ideally what it generates is something that already has the name field in it but let's go ahead and define that ourself so we should have a public name string all right so now that i have that i can start using that my user's controller by adding an import which references that model and then my i can update this get all to be into const users so since it's async we can await for user dot all and that's gonna do me uh that's gonna do my you know select star from user table and we'll just return users now i can test this now but i know it's going to return me an empty array so let's kind of make our create more real by actually creating uh or doing an insert so there's a section here in the documentation under crud operations we're going to look into how to go about doing a create so it says that you can either instantiate the class and provide the values directly like this and then you have to do a save or what i kind of like to do is this create method where i just do something like cons user equals await user model for eight and then i pass in my data right my dto and then i just return back that created user all right so let's kind of see this in action back in insomnia so back in my adonis api here on slash 333 if i create a user with my name on it and hit send it brings me back an object which has the timestamps as well as the id and just to check that this is auto incrementing i'm going to do marius 2 here and do send so i've got id2 now on the right now when we switch to our slash get get slash users we get back those two users right so so those two things are now working uh and also let's update the rest of these methods to work properly so the get by id looks like we just need to do something like this with a user find and we pass in the id right and then we just return the user again for updating it looks like what you first have to do is query the user make your updates so for example let's add a type here maybe we want to do request and say that we're trying to pull um the name from the request which i believe you do request that only name so let's say that we're trying to allow the ability to update the name so we'll get the name and then you know probably ideally you do the same validation in the middle here right but we're just going to keep it simple for now and then you can just do if i understand it correctly you kind of just manipulate the object and then we can just await user.save and looks like this just returns the user so why don't we go ahead and just return that and this is complaining to me because again it doesn't have the type for the uh the request body so maybe it is better to go through the schema again so let me just copy and paste this validator so that it provides me with a type so let's do and let's delete this and we'll do user.name equals that name and i do like that it tells me it's possibly no because what if you didn't find the user right um i'm wondering if they have a find one or fail here which they do uh so we'll grab that we'll change that to find one or fail so what's gonna happen here is if they don't if the model doesn't find the user in the database it's just gonna return it's just gonna fail at this line and it's probably gonna throw an exception so it's never even going to get to this part right and then i guess this question mark is no longer necessary we just do return await user save all right and then our delete same thing looks like you just find the thing and delete it and we'll just return that all right so that's how you do basic crud with the models in adonis using the active record pattern um i did test this in insomnia i'm not going to show it on the screen just to save you guys some time but just trust that it works right it does the delete that does the name updating and you've already seen the get all and get by id and create right so that all works all right so let's go ahead and switch over again to nest js to accomplish the same thing uh and let's take a look at how that's different so i mentioned with net ces it is very much database agnostic you can do whatever you want you can use whatever orm you want it has support for sqlize prisma you can use mongoose with mongodb i think it's main integration that it promotes is typo rm so that's what we're gonna use and this i think provides us a direct comparison to lucid but yeah do catch the point that adonis only has lucid so if you don't like that orm or if you want to do i don't think that you can um you could probably hack it to work but it's not intended to go outside of that space alright so we got our red nest yes code base on the right documentation on the left i'm gonna do i'm gonna go ahead and install an sjs slash type orm type orm itself and my database driver which in this case is also sqlite the next step i took is i made this new orm oremconfig.ts file in the root and i added this stuff which basically just says i'm using sqlite and here's my where my migrations will live and here's where uh the cli will dump will create new migrations i did have a recent video that talks about uh typeorm in detail so you might want to check that out if you want a little bit more clarification on what i'm doing here but so i'm kind of doing it a little bit faster than usual in package.json i also added new scripts and this is all covered in the documentation as well as in my previous typewrm video but basically there is a new script here that allows us to generate migrations and to run them if you notice from the adonis side they have a cli kind of built in for that right they have their own commands in this case we just um there are commands for us to do this stuff through type 4 we're just creating npm scripts so that it's a little bit easier all right so once i have all that stuff specifically this config i then need to go into my root app module and i need to do a type orm module.for root and i need to pass in my config here which we're just gonna import from that file that i just showed you so that effectively allows me to connect to the database and then from there again kind of similar thing we need to be able to create our users table right so in the users directory we do have a user entity which is empty right now this is the class which represents effectively both the user model as well as the table itself so it's a little bit different from the adonis approach but let me type some stuff higher and it'll make sense in a little bit so i'm going to mark this as an entity and then i want this to have an id which is a number and i'm going to say that this is a primary generated column which by default is going to do auto incrementing ids kind of similar to what we did in the adonis side we said that we want a name and i'm gonna i'm just gonna make that a basic column and then in nest you can also do something similar with time stamps where you can do uh created at created at date and then there is a special decorator that you can use to do time stamps so for example there should be a create a date column and there should also be one for updated at which is the updated date column all right and then what we're going to do next is once you have your your entity you can then generate our migration using our new command so i'm going to do npm run migration colon generate dash dash and then i'm going to provide a name so i'll just call this user right what happens next is it generates this new file for me if i look into it here's where the the direction kind of differs a little bit from typo rm and lucid is that typo rm allows you the ability to auto create your migrations if you wanted to and you can do it manually as well but i personally really enjoyed the the ability to auto generate it for you so for example running that command it looks into all of my entity files and note and it kind of does a diff with a database so it notices that oh we don't yet have a user database which maps to this entity class let's go ahead and create a migration to create that user table and based on the columns that i define here it's going to create the same columns in the database so you'll see a create table with id or incrementing it's got a name varchar it's got created date time and updated date time and you'll see that it defaults to now on create right so similar thing with migrations except that it has the ability to kind of auto generate stuff for me the difference the core difference is that in typo rm the the model file the entity class is sort of the source of truth of what is the schema right you saw that i defined this first and then ran my my migration whereas in adonis the migration sort of becomes the source of truth of what is the shape of my database all right and we'll do a deeper kind of compare side by side but first now that we have this let's take a look at how we can access this for querying in our application so within my users module i'm going to add a new imports here and i'm going to add typeormodule.for feature and this takes an array of entities which i'm going to add user to and what that basically allows me to do is within my service i then now have the power to dependency inject this into the user service so i'm going to add a constructor and within this constructor i have this kind of special decorator that allows me to inject a repository of user so i'm just going to call that user's repository which has a type of repository user and make sure to import user all right what that then allows me to do is i can start using that user's repository to do queries against my user table and i forgot to add private here so that we have access to this as a property of this class all right now that i have that that means i can now start utilizing it so how do we do our create first of all we do our new user equals this dot users repository dot create and then we pass in our fields which in this case is create user dto right if you remember it's just the name and what this create effectively does behind the scenes that it does something like a new user and then it you know it assigns the user.name equals dtl.name right then once you have that in typeorm the way we kind of actually officially save this in the database is through save we take that new user and we save it okay and then for find all this simply turns into this dot usersrepository.find that's equivalent to doing a you know select star from user and then kind of similar to the adonis method there is a find one or fail here which you can just pass in the id the update again kind of similar strategy where you first need to query the user so we're gonna do a wait so this should be async uh we'll actually just reuse our uh find one pass the id into that so we're gonna get back the user from this and then you can then manipulate that user so for example we wanna do we wanna allow name updating so we're gonna do nate user.name equals update user dto dot name and update dto user simply extends create user dto so that has the same fields which is just name and then same thing we're just going to do a save this dot user's repository that's save user and remove kind of same thing you query the user and then you do a remove this dot usersrepository.remove user and this should also be async all right so there is our crud and again i am not going to show the testing that this works just trust that it does um i do again have a typo rm video where i show and explain a lot of this stuff in much deeper detail so if you're interested in that make sure to check it out all right so let's go ahead and do a compare and contrast so the core difference i think when you kind of put these guys side by side is a lot of the methods from an api perspective are pretty similar right they got defined um right and they have find one or fail and then they kind of do the same pattern where you first query it and then do your updates or deleting you know it's effectively the same thing the core difference is that in adonis they prefer the active record pattern which the gist of that is that you're working with the model directly there's methods on the model like you have user that find in sjs or with typo rm and actually typorem actually does also support the active record pattern but for an sjs specifically they kind of promote the use of the data mapper pattern which is where you have a separate entity which in this case is the repository which kind of takes a model and then kind of allows you to work with it so instead of having methods on the model directly you're doing you're using methods from the repository and there's a there's an important benefit to doing it that way from a testing perspective because you know if you're able to do dependency injection right if you're thinking about this notice that we're injecting the repository of type user we're not specifically injecting the user model itself that means that in testing we can very easily mock the repository itself whereas in the active record pattern i'm not sure that you can do that as easily and again i think i mentioned it earlier that in the adonis documentation there's really nothing there about testing so it's not clear how how much easier or harder it is to test the active record pattern in in this scenario so in conclusion would i recommend adonis over nest i think this is a very heavy uh it's up to your preference if it was me at least with what i currently see i would say no and for a very specific reason first of all the v5 is in preview so it's not quite ready you can use b4 which is already which is actually pretty similar to v5 v5 just has a couple extra enhancements but the the problem that i'm seeing with this framework is it's it's very much an extremely opinionated um approach to the solution so if you think about it nest and adonis are effectively trying to solve the same problem except that nest is okay with utilizing what already exists it utilizes what's already built by this amazing ecosystem of express and node.js and it just focuses on the one thing that it needs to improve upon which is application architecture as well as the use of typescript and they nailed that very well in my opinion whereas on the other hand adonis tries to reinvent the wheel in almost every possible space and in my opinion this just slows the whole thing down so there's actually this graphic that i'm going to pull up from the book essentialism and in that book they talk about this idea where your you know your energy is limited if you're splitting up your energy in all different directions like here on the left then naturally you just don't really progress as fast as if you just focused on one direction right and this is very much if i change the label here the left side would be adonis and the right side would be nest ness decided to just go forward and solve the problem that they think they need to be solved whereas adonis which is pretty much run by one core author um has decided to really reinvent a lot of things and i understand kind of the vision behind it because if you kind of custom build the different components then you you can imagine that they're going to integrate very well together they're going to have the same language they're going to have the same design decisions and they're all going to come from the same author right so they're just going to integrate well but it also means that it's just going to take a long time for this author to build all of this stuff from scratch and i don't think that's a good strategy in a world of open source where you know there's so many good engineers and projects that if you're always have the stance of i can do better you know you're you're i feel like you're just not going to get anywhere with that said i do think that the author uh of adonis i believe his name is har minder mr burke he's obviously an amazing engineer right the fact that he's able to build all these different components you know himself you know think of any engineer that had that knows how to build all of these different components that's pretty impressive and he's already on v5 he clearly has fans um so if you believe his vision uh his patreon is here make sure you go support him and he does talk about his vision a little bit more in here right so if you look in here it does talk a little bit about sort of his reasoning about why adonis should exist you know he you can tell he's saying there's nothing yet in the space that really is similar to rails or laravel so he's trying to emulate that as well as he talks a little bit about okay why can't you just utilize existing stuff um you know in his case it's a difference in values in his in his mind it's better to kind of build stuff from scratch that integrates well together right so he's saying in theory pulling packages from npm sounds great but making them work together is often requires a lot of effort but again in my opinion sjs is an example of pulling that off well but with that said i do believe that adonis once this guy kind of reaches his full vision for it i think it's going to be an amazing framework you know all the stuff that he's doing where um he's doing inversion of control through simply the imports like this that's amazing and if you kind of compare the code from this controller to this nest controller right like the nest code does get kind of a little bit bloated after a while with all the decorators whereas in in adonis it's very simple it looks like you're mostly writing you know javascript you know like basic javascript and it just looks very clean and nice but again as you saw throughout the video there's just little things that i wish were better the documentation and stuff like that um testing strategy and again it is in preview so i so i can't kind of say too much bad about it but i do think maybe in a year or two when it's fully released and the community has had a chance to work with it i think it's going to be a really good framework that you you definitely should keep your eye on but for today would i recommend it i don't think so would i recommend v4 maybe if you really like what you see in what i went over you know again v4 is still pretty similar so maybe you can start there but i don't see the benefit of jumping into v4 when it very clearly is about to kind of evolve into v5 and i don't know how much time um anyways yeah if you if you believe in this guy's vision make sure to support him anyways guys that was a super long video i think this is probably the longest video i've made so far if you've been with me throughout this whole thing i super appreciate you uh consider hitting the subscribe button if you want to see more of this stuff and please let me know in the comments what you think i very much appreciate all the comments that i've been getting i mean it kind of helps me drive the direction of this channel if there's a lot of people that like the nest content then maybe i make more nest content without said i think that's it for me if you like this video hit the thumbs up and i'll see you on the next one [Music] you
Info
Channel: Marius Espejo
Views: 15,303
Rating: undefined out of 5
Keywords: nodejs, adonisjs, javascript, nestjs, typeorm, expressjs, adonisjs vs nest, nodejs tutorial, node js, node.js, adonisjs vs express, adonisjs vs expressjs, adonis vs nest, adonisjs vs nestjs, typescript, nestjs typeorm, sqlite, node js tutorial, nestjs microservices, adonisjs 5, node js frameworks
Id: Zfl4Wwcqfoo
Channel Id: undefined
Length: 94min 31sec (5671 seconds)
Published: Sun Mar 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.