Setup | Node.js API From Scratch Using TypeScript, Express And MongoDB #1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome long time no see uh i'm back after like four months with a node.js api tutorial um in this tutorial we're going to be focusing on using typescript node obviously express and mongodb it's going to be a series of tutorials and the idea is that each tutorial is going to cover its own specific topic so the only video you will have need to needed to have seen is this video which is the setup video and every other video you can watch just individually you don't have to watch them all you can just watch one or you could watch them all and it'll basically go through topics like authentication uh cues potentially websockets stuff like that all to do with creating apis in node.js so hopefully it's useful and it should be quite a long running series all being well so in this video we are going to go through actually setting up a project um we're going to get everything we need installed we're going to create essentially a framework of for which we can use later on in other tutorials um i'll put it on github so you can use it yourself essentially at the end of this tutorial you can take what we've done here and just go crazy start building your own apis and use this as like a backbone for that um [Music] there is one thing to bear in mind and i know people say like oh you should do something this way or something that way there's a million different ways to do things especially with what we're doing where there's no real structure we start just coming up with a structure on our own there's a million different ways you could lay out a node.js app or any app for that matter um different plugins uh you know npm packages and whatnot some might be better than others and you might not like some of the things that i do that's completely fine um if you want to improve upon something or you feel like you can do something better go for it feel free to leave comments any improvements um and i can go over them later on or not but you can just do whatever you want to do this is just going through how i do things and this is basically a culmination of quite a few years of using node.js and reading a lot of different um blog posts and and seeing how other people do things and this is just how i lay out pretty much every project i work on that's based around node.js so without further ado let's get into it and the first thing we need to do is start off with a blank project um i've got this node.js from scratch project i'm just going to open up the command line so the first thing we need to do is do npm init dash y to start up our npm project and this will add our package.json file so after that we have quite a few things to install and we're getting a lot of stuff that we need on this project sort of installed at the beginning to get it out of the way so i'm going to run through them um what i'm going to do to make it easier for viewers is leave all the npm stuff in the com in the description not the comments but in the description below the video and so you don't have to just write everything out i think that'd be a lot easier but it is important to take note of what we're installing um so i'm just going to do npm npm in it not in it just i d and actually sorry let me just mic a bit there we go um so this will install everything as dev dependencies because there's this dash d so i'm going to copy and paste stuff so just start to write it all down because there is a lot of stuff to go through here so the first one is obvious typescript um we're going to be using typescript for this i i'm going to assume that you have knowledge of what typescript it is if you're watching this video it's a bit out of the scope of this video to go through it but maybe in the future i can do a tutorial on it if people are interested in that the next thing we're going to be installing is tsc watch and what this will do similar to nodemon if you're familiar with that it'll it'll watch when we make changes in our typescript files and if it does it will run a command for us in our case we'll rebuild the project and essentially restart it and then it'll use the new code then we've got eslint which if you're not familiar with that it will highlight your code and tell you any errors um depending on how you set it up so for us we'll be setting it up to look through typescript code and tell us any potential errors with our typescript code then we've got prettier which is a plugin that will reformat our code depending on how we set it up so we're going to set it up well really you can set it up however you want but i use it when i save the file so when i do ctrl s for example it will reformat the code into um proper formatting code so so essentially none of your code is a mess uh although that's the hope anyway um the next is eslint config pritier and that's just a config for eslint i think it's in prettier we use that or is that i think that's in eslint but so that it knows that we use them pretty something like that um eslint plug-in prettier i don't know 100 sure what that does um a lot of this i just got from my projects which i set up quite a while ago um typescript this is the parser for eslint for typescript and then we've got the plugin for that as well then we've got types slash node so obviously we use node but uh unless you add the types for node it will understand um typescript will understand what you're doing when you're using node specific packages um so we just add that then we've got app types express same thing for this um we'll be installing express in a second this will just make it so that it knows um it adds types for express so when we use anything related to express it has the types there and and um typescript will kick up a fuss so we can install all that quite a lot again i'll leave it in the description um but this will get a lot of the stuff we need out of the way a lot of the setup stuff and a lot of this is just to make our code writing a bit nicer uh is that no issues there i don't think i think we're all good yeah so that's all installed uh the next we've only got two more to go so npm i this time to install them as normal dependencies and i'll write these ones we've got express which is obviously our routing engine and then we've got end which is um it will take end variables from our end file and put them into process.n so that we can use them without throughout our node.js application oh again a lot of errors here i think oh i think that's fine um so the next thing we need to do is actually get our config file for [Music] typescript so we do npm [Music] or mpx actually sorry mpx tsc dash dash init and that will create our config file here we go so we've got tsconfig.json now so a couple of things we need to do here where is it we're looking for base url here we go so our base url is going to be not dot slash but dot slash src so all of our code is going to live in the source folder so that's fine let's check if there's anything else we need um there's one thing oh yeah out directory so that's called out do wherever that is should be here it's not there though hang on a minute let me do a search out there we go i don't know why that was so hard to find so out directory is going to be dist so this is where the code that we've compiled from typescript is gonna live so this is essentially where the vanilla javascript is going to live and this is what we you would actually run in a production environment and just in general this is what node.js will actually run um there's one more thing we have to do and it's to do with module aliases which should be just under our base url stuff and find it where's paths is it up here here we go okay so paths now this is an interesting one um paths will basically make it and i'm just going to copy and paste what i have here for this for this part and then explain what they're actually doing so it probably is pretty self-explanatory but basically we're gonna have some folders and so resources is where like our controllers and models and stuff will live utils is where utilities live and middleware where middleware lives the problem with that is if we need to get code from these we usually have to um do the relative path so that can end up being something like this and i'll just mess up this code quickly but you might end up doing something like this import something from and then you'd have to do depending on where you are in the code dot slash dot dot slash dot dot slash resources now that's a pin in the ass so what would be nicer is if we could do something like this at resources from anywhere in the code and that's essentially what we're doing here so let me just unmess up this uh so that's what we're doing here we're just basically aliasing at resources to resources and it'll be the resources from i think it's it'll use source as the root whichever we put as a root directory i think or a base url so that's it for our ts config that's all fine um what we'll do now is in package.json we will add our scripts so scripts are what we're going to use to actually run the code um so let's add start so start is going to be the script you run in production um not when you're developing but this will run the actual code that's been compiled from typescript into javascript so we'll do node dist and then our main file is going to be index dot js so that'll be the entry point so when you go to launch this into production you'll run you'll want to do uh npm run start then we have dev this is what we'll run locally so we'll do ts watch and then sorry i've got that issue again where the mic is in front of my keyboard so i can't do anything right success backslash to escape this node dot slash dist slash index.js and another backslash to escape that speech mark and this is what we'll run in development so we do npm run dev and what it'll do is it'll keep an eye on our typescript files and anytime anything changes it'll rebuild the project and restart our server we've got build and this is just going to be tsc so if you run this that will build your typescript project so essentially um compile it down into i don't know if it's compile or transpile or i'm just gonna say compile uh compile your typescript into javascript into the dist folder um and then what we're gonna do is add something more out of convenience but if we do npm run build so this post install will run after you do an npm install and that's quite useful if for example someone pulls your api from github or something and when they run npm install it will automatically build the project for them which quite useful i think in production environments as well let's say you pull from your source control and do an npm install as part of your deploy you will automatically build the project so it's ready to run straight away which is quite useful um alternatively you could just run npm run build like just manually after that's fine as well but this is quite convenient to have um so that's the package.json pretty much done what i'm going to go through now is eslint and prettier setup um now a couple of things you'll need for this if we look at my extensions i've got a lot of rubbish that you don't you care about but eslint you'll need eslint the plugin for that if you're using vs code that is and the prettier plugin as well um i'm not going to cover them completely in this tutorial because this is just part of the api setup you can if you want to skip this part maybe come back to it later or look at just setting up prettier and easily on your own that's fine um they're not vital for this it's just nice to have them so those plugins you'll need to install there's probably going to be an equivalent of those plugins for whatever text editor you're using so you'll need to look into that i'm just using vs code and you can just find them by searching up here um so let's just add the files there's two files we'll need for this there is dot es lint rc.js i think i spelt that wrong yeah that's uh quite normal for me eslint now see there we go and then another file which is going to be fairly similar but prettier rc dot js there we go so let's do prettier first um prettier is going to be module dot oh i just did okay that went well uh module is it gonna try and okay exports yeah that's uh some sometimes vs code will just try and fill things in for you and it's usually not what you want at all um so sorry about that tab width is going to be 4 so it's going to set the tab width for our files um single quote this is all like completely down to you by the way you can choose whatever you want to do this is just a style that i follow single quote gonna be true if i can spell that right and then semi is gonna be true and that's talking about semicolons so now you know javascript doesn't necessarily need semicolons technically but when i'm working on the backend stuff i prefer to have semicolons um but that's preference although i think in typescript that might be false i'm not 100 sure about that one eslint um pretty much the same thing in terms of the module that exports uh we're going to add the passer so this will this is where we use the pass that we installed at the beginning so at typescript and it's dash yes lint what can i not spell it could have something to do with the fact i can't see my keyboard uh extends i really need a bit of mic set up plug-in typescript dash yes lint oh yes slash recommended oh my god that was the worst spelling okay good um i might just copy these last two because i can't type oh this could be a nightmare um that so the next two prettier slash at typescript eslint and plugin call on prettier recommended pass options that's an object uh ecma version 2018. sauce type is going to be module because we're going to be using modules and rules is going to remain empty for now and that's pretty much it done for our setup of yes lin ts config is throwing um oh yeah okay so it's throwing an issue [Music] um i think because we don't have any so we don't have a dot source folder i think that's part of it but that's fine we're gonna add that later um the next thing we're gonna go through is something quite important um especially if you're planning on pushing to uh source control so like github git lab bit bucket that kind of thing so that is going to be a dot get ignore so in here we want to ignore our dist folder because we don't want to be pushing that um it's a bit unnecessary because you could just use the typescript code to build the disk folder there's no real need to push the dist folder um the same with node modules um all the modules you need are in package.json so you can just install them there's no need to push them uh that would be a lot of code to push and the next one which is more of a privacy thing is dot end so you don't want to push it don't end because that's going to have all your information like your api keys that kind of thing um so you don't want to push that but what we'll do is create something we do want to push to do with dot end and that's dot end dot example um so what will go in here is basically an example of what your end will look like so for us we want to have a node node n equals development part and these are just going to be some default values so 3 000 then we want to do i'm going to leave these ones empty path equals and this is going to be all the setup for mongodb user equals and then password equals so a divided example will go into your sas control um it basically just gives users um like other developers an example of what they need to include um and then we're going to create a dot end which we can fill out now i'm actually gonna um copy and paste because i've got i've got all the information so this is my mongodb stuff um two ways that you could use mongodb either through docker which i have something here which might be useful for people if you know how to use docker and specifically docker compose this will be useful for you um there is another file which is related to this part here which is init.js so this will create a user um it doesn't seem to work for me i haven't debugged it yet so i'll have a look uh into what causes that but um yeah this should work fine and then you basically just use the admin users it's a local database there's not much of an issue um but this should work if you're using docker uh if you're not using docker then mongodb atlas is probably where you want to go and i'll go through it a little bit later not not atlas but i'll go through the potential a couple of potential changes you need to make in regards to this um path here uh not really this one but this path is essentially gonna be used in the code um but we'll go through it later but yeah you could use mongodb atlas which is uh mongodb's sort of cloud hosting platform and they give you a free mongodb database so yeah i might i might end up leaving a link to that in the description if you want to use that that's probably the easiest way to do it if you're not familiar with docker um so how you it's a bit out of the scope of this video but how you want to set it up it's really up to you but yeah this is where you put your information for connecting to mongodb um so the next thing we need to do is add the module alias package so we'll do npm i module alias so this relates to the thing we did earlier in the ts config file um if we go down here we can see these paths here basically when the when they're done in the ts config they only apply to typescript and you you need the vanilla javascript equivalent in order for them to work properly so these ones we use in typescript and then the ones that we're going to add in here which we'll add now remember how to do it is uh these ones are going to be used in the vanilla javascript at least and it's basically going to be the same as what was in our typescript because if they're not they wouldn't work so you you have to make sure that these these ones at slash resources are the and utils and middleware are the same as the ones in here at slash resource as you can see so [Music] so yeah the only difference really is that in the case of these ones here they're going to be referencing the dist folder because in ts config it's going to be using the source folder and then in the javascript equivalent uh it's going to be using the dist folder so the next one we're going to do is create the actual structure of the um project so now we're going to finally create the source folder so if we do dots just sauce um i was thinking of doing pasta then um then we do middleware so this will be where on all our middleware goes and then inside of source again we do resources and this is where all the resources go so in our example today we're going to be creating essentially blog posts um and for that we have a post resource which represents a blog post so that's just an example of one resource that you could have you could have users um for example and then one more folder in oh let's create a file one more folder is going to be utils and this is when where things that are useful to us but aren't really related to any specific resource are going to go so we're going to have interfaces in here and stuff like that and useful functions that we're going to use throughout the code but they don't really belong in middleware or resources and then two more files that we need to create we need to create app dot ts and this is going to be not technically the entry point into the application because that's going to be index.ts but it is going to be the setup so we're going to set everything up we're going to initialize mongodb all of our routers and stuff like that last thing we need is index.ts and this will be our entry point so the first thing we're going to get on with and this is where we actually start writing some code so quite exciting is um right in our app.ts file that's a good place to start so we've got to app dot ts and we're going to import some things that don't exist yet so don't worry about that just follow along with me and what we'll do is we'll go through all the stuff that doesn't exist and add it um a little bit later so that's fine so we're gonna import express and then we're also gonna import from the same package uh application so that's from express we're going to import mongoose from mongoose and that that's not installed yet so we'll go through that in a second and we're going to import compression which is a middleware for compressing the requests and we'll import that soon as well probably would have been better to impart him first so that we could use autocomplete but that's fine uh import cars from cars again this is another middleware that we're going to be using um this will set up cars so it'll allow requests from like different origins and stuff um in part morgan from morgan morgan is a logging tool which is quite useful um useful for us in development because it will give us an idea of the requests that have been made and things that have gone wrong so it's quite useful um import controller from and then this is where we finally get to use uh early so you can see how useful they are at utils slash interfaces so we haven't created this yet but what we'll have is a folder of interfaces that are sort of just not really related to any specific resource so in this case it's going to be a controller interface and that's going to be controller dot interface so that sort of our structure is you have the name and then like dot what it is dot ts so so we have controller dot interface so it's an interface it's just an easy way of um keeping track of what things are so at a glance you can see oh that's a model that's in control that's an interface for example um import error middleware which we'll create in a second from at this is going to be middleware and then error and dot middleware so we can tell that that's a middleware and then one last thing which can be a security-related thing is import helmet from helmet so helmet will help us help prevent common attacks um that would be made against an api for example so [Music] what we'll do is create class called app public express this is just going to be the type of application which we imported up here and then public part and that's going to be a number and this is eslint doing this by the way it's telling us that we've initialized a property but it isn't being used and it hasn't even been assigned yet so we're going to actually assign this in the constructor constructor so we'll do constructor and this is gonna accept two arguments the first one is gonna be controllers and this is gonna be type controller but it's gonna be an array of controllers and then part number and inside of the constructor we're going to do this. express so we're going to finally assign express to express so this will start up our routing then we're going to do this dot part is equal to part and then we're going to have some more functions that we haven't actually defined yet so this dot initialize database connection this dot initialize middleware okay sign and then this dot initialize controllers and we're actually going to pass in controllers for this one and what this will do this function will loop through the controllers and it'll make a little bit more sense later when we actually create a controller but essentially what controllers will actually contain is the routes that they use and then what we can do with that is like add them to express so express knows okay that's a root that exists and it knows where to route it to um so it make a little bit more sense we actually come to write this function and the last thing we need it's important this is last is initialize error handling now as far as i'm aware when it comes to express error handling the error middlewares the error and handling middleware has to come last so yeah i'm adding that last um initializing that last on this um if we go through and it's probably better that we install everything we need now and then and then keep going because all these red lines it bothers me so i'm gonna i'm gonna install everything we need now um just to get rid of all of these red lines so i'm gonna copy and paste again but what i'll do is put the command in the description like i did with the other one so we need mongoose we need compression we need cars we need morgan and we need helmet so those are our normal dependencies that we need to install and then we've got a few dev dependencies as well just for the types for some of these um so if i do mpmi and then dash d instead of um what it was before just mpmi so we need that dash d for dev dependencies and we do at types compression at types cars and at types morgan some of these have the types included in them in their um package so we don't actually need the types for those ones like i think ex i think did we oh no we installed express earlier with types yeah um i think mongoose is what i'm thinking of which has types added by default so we don't need to install types for them yeah that's got rid of a lot of the red lines um we just need these two bits of code here which we can add a little bit later um what we'll do is we'll get started on the rest of these functions so the first thing we'll do is write private initialize and this will be middleware and we need a return type on these because typescript is in strict mode strict mode at the moment so it'll be it'll tell us off for not having a return type but in this case we're actually not returning anything so it's just going to be void and now we'll just add all these middlewares that we need so we need express dot use i'm gonna start off with helmet and that needs to be call like that express dot use cars this dot express dot use again and then we'll use morgan sorry the fact that my um mic is in the way is making it really difficult to type it's not good for a tutorial where essentially all you're doing is typing once again it's not express dot use this will be express dot json so what this will do is it will take any um json that comes through as like a post request for example and add that to i think it adds it to body i don't know if it says if i hover over it yes it'll pass json request essentially um a similar thing that we're going to do now but instead it's going to be for farm encoded stuff so our url encoded sorry url encoded and then extended false okay um and then the last thing is going to be use compression so that will compress our requests to make them smaller which is always nice um the next thing will be another private initialize controllers and of course we've passed in controllers to this one as well so the type is going to be an array of controllers and the return type once again is going to be void and what we're going to do with this is do controllers dot for each and then controller it's gonna be a type of controller and then an arrow function this dot express dot use and this um this path that we're gonna have add here we're gonna do slash api and that will be the base path path of everything so for example later on when we add our posts controller it will be the url will be slash api slash and then posts and then that will be the same for every other reason it'll always start with slash api so you can change this to whatever you want really api and then if you have a different version you do like v3 that kind of thing and that just makes it a bit more convenient and then we'll do controller.router and we'll add error handling as well initialize oh initialize error handling once again this is going to be void this dot express dot use error middleware and again we'll define that soon um second to last thing we need is another private and then initialize database connection so this is where we initialize our mongodb connection so we're going to do now is get um some variables from our end process.inv so const i'm going to destructure the object i'm going to do we're going to get user password path and that's going to come from process dot end and we're going to add some stuff a little bit later on we still need to add end um as in in the code so it actually includes all the end stuff and i've got some i've got something else that will validate that we've got all the env end variables that we need um but we'll add that a little bit later on so now we're going to do mongoose dot connect and this is where things kind of potentially differentiate i think you can do it the same way but basically if you're using mongodb atlas the only difference for you will be that you do mongodb plus srv something to do with like dns related stuff so for me i'm using docker so mine's set up to just do mongodb call on slash slash if you're using mongodb atlas you can do srv plus srv um but i think you can do just do this how i'm doing it now as well and it should work fine but that's something to bear in mind just in case so the first thing we need is user so we're setting up the string now the connection string then we need the password for that user so we'll do password then we need the path so we can do path and you can see how it's using our end variables to set up mongodb and that should be it for that one more thing we need and this is to start everything up really is or at least make it accessible um to the outside world is listen so if you ever done express you'll you'll probably know what i want to do but basically i'm going to do this.express.listen and i'm going to pass in this dot part which is what we defined earlier and then a function as a second parameter just to say that the app is up and running app listening on part oh cat type and then this dot part there we go and perfect so the last thing we need to do is export that so that we can use it later on app okay so before we move on to writing index um what we'll do is cover writing these this interface in this middleware so we do that now so we'll need inside of utils we'll need a new folder called interfaces and inside of that a new file which will be controller dot interface dot ts and a fairly simple interface really we're going to import router from express we're going to define the interface controller and that's going to require path and that's going to be a string and it's going to be router and that's going to be a type of router so either we can put the export default here which i think i might do actually i'll put it below it depends how you want to lay it out so for example put it here which might i might just leave it like this but really it's up to you how you want to do that um i tend to put the export default at the bottom if i'm dealing with like classes or really anything big i'll put export default at the bottom just to keep some code out of the way it's not a lot but some of the code out of the way um and then interfaces if they're small interfaces i might just put them export default there just before the interface control up to you what you do i'm going to leave it like this but really preference um the next thing we'll do is add the error middleware um so if we do a new file in middleware and call it error dot middleware.ts and we're going to import a few things from express again so we're going to do x we're going to do request sorry request response next function and then that's gonna be from express and then we're gonna import something else and this is annoying because we don't actually have this thing yet this is another thing that we're gonna have to create ourselves but it's going to be an http exception from and it's going to be from utils exceptions so another folder where we ex we're going to so we've got the interfaces folder and then we're gonna create an exceptions folder to store exceptions um we're only actually gonna create one for now but if you ever need more exceptions in the future um you can store them there http dot exception so let's quickly create that so in utils create another folder called exceptions and a new file called http dot another fairly small file thankfully class http exception it's going to extend i think it's node's default error or just javascript's default error i forget which one it's going to have a public status which could is going to be of type number and this will be the status so like 400 or 200 or whatever of the request um public other response public message that's going to be a string and then we're gonna have a constructor and that constructor is gonna accept two arguments both the status and the message so we're gonna call super to call the constructor on the um error and that's gonna accept the message um so essentially what we're doing is creating an error but specific to http exceptions so we'll do this dot this dot stairs equals to status and and this dot message is equal to message okay so again export that and like i said i put usually when i'm dealing with classes i'll put the export default at the bottom just to make it it feels a bit cleaner to me i think instead of having more text on the top that just looks cleaner to me but again up to you um we'll go back to our error middleware and we should have that now uh it may not update immediately so if we just do something like this exception there we go so now it recognizes that we have it um we'll do a function in here and it's going to be the error middleware function which is going to be the same as pretty much any other middleware that you would use um but we're just going to pass in error as well that's going to be a http exception um rec is going to be [Music] request is going to be response and next is going to be next function and that's going to return void there we go so we'll do const status and we'll do error what's this um if we don't have that we're just going to put it as a 500 by default and const message is equal to error. error.message or if we don't have that we'll just put something went wrong and that's just gonna be default if something fails and we don't know exactly what fails we'll just return something went wrong to the user and they can contact us apart and complain that's okay and we will do res dot status or passing the status oh status dot send and what we'll do is we'll send the status code and the message all right and then we need to export this so we can use it later so we do export default error that's lowercase sorry error middleware there we go okay so that is i think everything we need for our app.ts uh error middleware let me just check why that's not working oh right okay i think i know why so yeah so that's actually you're not meant to put that as a function it's just uh yeah you just pass it in like that so get rid of the parentheses that you put on there and just pass it in that should work uh next thing we need to do is get index.ts so the first thing we need to do and it's important that we do this before everything else is import dot end so if we do end config so essentially what this will do is it will take all of our variables we put into here into our end file and then add them to process dot process.end so it's best that we do this as soon as possible so that we have them available to use throughout the application uh the next thing we do again we need to do before everything else is import module alias slash register okay and that will register all of our module aliases so that can be used again throughout the application next i'm gonna do is once again i know this is gonna annoy you we're gonna import something that doesn't exist yet but we'll write it in just a second um so we're gonna import validate end from and then we're going to use oh utils again at utils slash validate and so on right now in just a second we're going to import app from dot slash app then we're going to import oh no we're not going to import yet so the next thing i have on here is post controller which we're going to create a little bit later but i'm going to what i'm going to do is i'm going to save that until the end and then we'll import into here and then set it up so we'll leave that for now um validate end is just going to be a function that we call like that and what that will do is it'll go through end file and check that we have everything set that we need but we'll write that in just a second we'll do const app equals new app so we'll initialize it here um got an empty rear this will be the array of controllers so like i mentioned a second ago the post controller is the one that we're going to create in this tutorial and we'll import when we actually um create it but we'll this is where we pass it in so it can be used in the setup process then we're going to pass in a number which is going to be process dot end dot part so that'll pass in the part and um then we're going to do app.listen oh app.listen there we go um and that's where we will you know expose our api to the outside world and allow it to be accessed through whichever part we specify so what i'm going to do now is create this validate.end so we're going to create this in utils so we do a new file validate end and validate end it's quite useful it's it's one of those things where it's a little bit of a hassle to set up i'm not really much of a hassle but it's like oh why would i set it up set this up seems pointless but it's actually quite useful so what we'll do is um we need to install something first so we'll do mpmi end valid this is a library that we're going to be using to validate the end um so once that's installed and go back and we do import again i'll leave that in the description by the way so you can just copy it from the description uh clean end so that'll be the function we use string and then part so these are two types of um two types that we're going to use to basically validate the values and make sure that they are the same as the types and i think that'll make more sense in a second so we'll import end valid and get rid of the hashtag um function validate involve the end this is going to be void again i'm going to use this function that we imported clean end and the first argument is going to be process dot end and so this is where we're going to this is where we're going to get the actual end files from because at this point remember we've already got them in process to end so what it will do is it'll check process.n and match them against essentially our validation rules that we're going to add now so the first one is node end and we want that to be a string which is why we imported str sorry um and that's going to accept an object and there's a plethora of settings you can do that you can pass through one of which is called choices and that essentially sets some potential choices that you can choose from and if it doesn't match one of these choices um it will count it as invalid so to give you an example the choices that we have for our end uh node end is going to be development or production and basically if we pass in something that doesn't match one of these two it will say it's invalid and it will it will run the application so that's this is essentially what this is doing is making sure that we have everything we need to run the application and without failing so the next one is going to be password and for this we just want it to be a string and we just want to check that it exists and we can do that just by passing string and the next one is going to be path once again we just want it to be str oh i can't type that again user once again just string i keep typing the entire thing um i'm getting too used to typescript now so i'm just anytime i need to do um some of type string i just put the entire thing um part is going to be part and we can actually do another option here and specify a default so if we do default and we can put that to 3000 so if in our end file um the part doesn't exist it will just say automatically to be 3000 so technically speaking we don't need to add the part um but we're gonna do it anyway i mean it already exists so we're gonna do it anyway any better that you do include it but if somebody doesn't include it not the end of the world it just set it to 3000. so that's that done um app.ts is done so now we can finally get on to writing some of the code that's because so this is so far this has all just been set up the the code that we're about to write is going to be actual sort of api code and functionality as part of the api so as far as we're concerned the setup is done now thankfully um took a while but once it's done it makes it everything more convenient now that we have all this stuff installed and ready to go um so now we're gonna start and actually write some code okay so let's get started on writing our post resource so um in your resources folder if you create a new folder called post and what we're going to do is create all the files we'll need and it's going to seem like a lot um but bear with me it's not as crazy as it's going to look um basically all the code that we're going to write is code that we'd have to write anyway and just split it into different files and it makes it look like a lot more than it actually is so the first thing we'll do is add the post controller.ts then we'll add post model dot ts then post interface not ts so you can start to see it's quite a lot um post service dot ts and last one post dot validation.ts now validation is to do with making sure that somebody passes in the right um parameters when making a request so if they make a post request to like create a post it makes sure that um a title and a body will exist so quite important but one thing to bear in mind it does require another middleware so we're gonna have to write it that at some point um but we'll get to that when we need it um so the first thing we'll do is write the interface so [Music] i think some people might store the interface in the model which would remove this interface file however um yeah i prefer to just have it separately so i can use it throughout the code without having to import the entire model so we'll do um import import and document from mongoose so this interface and we'll do export default up here instead um this interface is going to extend pull uh an extended document sorry extends document the reason for this is essentially right essentially we want to be able to access all of the um properties and methods associated with mongoose documents because essentially anytime we use post it's probably going to be a mongoose document so it'll have things like underscore id and that underscore underscore v um all of the methods associated with mongoose and we want to be able to access them and to do that we need to extend documents um you can actually click into it and see all the stuff that you have so it's got things like um delete delete one and stuff like that so that will just expose that functionality or at least it'll make typescript aware of that functionality um so quite simple we just need title string and body string nothing crazy um for now so just those two so that's quite easy and we'll do the model next um again it's gonna be quite simple so we'll do import schema and then model and that'll be from mongoose and then we're going to impart our interface that we just defined so post from and this is um this is a bit of a weird one because we're using our aliases so this is probably one of those rare cases where using the alias is actually longer than just using relative paths so just to keep things like looking consistent throughout the code base i'm going to keep using the alias but you may want to use a relative path because technically speaking you could just do this post interface um and that's fine if you want to do that and it's completely up to you but i'm gonna i'm gonna use the interface uh the module earlier sorry um just to keep the code consistent across the application and it doesn't add too much to it it's just yeah it just looks sort of the same as all the other files that use the earlier stuff which is going to be all of the files um const post schema and that's gonna be equal to new schema and inside of here we can do that title which can have a type of string it's going to be required true body body it's going to be type of string again basically the same as um the other one required true yeah so same as title what we're going to do is um we're gonna add timestamps to this um sorry the format this is one of the things that sometimes goes wrong with prettier where it does this it does a formatting when you don't really want it to because i want to do this so i want to do time timestamps it's true so if i save now yeah there we go so that's formatted correctly um export default model and this is going to be it's a template so we're just going to pass in the type post and then it's going to be the name of the model is post and then the schema it uses is going to be post schema and that's it for our model so now we can use on we can move on to our controller now the controllers are probably going to be the biggest bits of code you rank besides maybe services actually so we've got this um this post service here let's close that down um this post service here so this will contain like business functionality i think they call it business logic is what people call it and your controllers will be in charge of routing to that logic essentially so in here we're going to import some things from express so we've got router request response and next function and that's going to be from express then we're going to import controller uh the interface that we defined earlier and this is one of those times where the module alias comes in really handy otherwise we have to do dot dot slash and stuff so we've got utils slash interfaces slash controller into this and then we're going to import http exception from at utils oh no oh yeah that is yeah utils i thought we were talking about the middle one then um utils and then we'll do import and we don't have this yet but we'll add in a second validation middleware um so this will be used for our validation to make sure that users are submitting the correct information and this is going to be in middleware slash validation dot middleware quite simple middleware and we'll add in a second import validate from and this is going to be at resources post post dot validation so we'll write that in a second and post service from at post slash course dot service so that's everything we need imported um what we'll do is create a class called post controller and that's going to implement controller and basically interfaces will just tell us what we need to include on this controller when we implement them so we'll do if we hover over that we can see it's missing the following properties from type controller path and router so we need public path and that's going to be equal to slash posts and remember we had that api that slash api earlier in um app.ts so you can see we're doing slash pulse here so that'll be added on to slash api so when we want to access this it will be slash api posts and then we want public router equal to router so we'll create um i believe they call them subrouters and that subrouter will be added to the main express router we have a constructor and that's going to be this dot initialize routes so um in a few minutes when we've finished all the code we'll we'll actually instantiate this class inside of index.ts and when we call that i'll initialize the roots because it'll call the constructor um so quite useful for setting things up so we'll just add this um function for now we won't add anything yet but initialize roots and that'll be returning void and yeah we'll just leave that blank for now and what we'll do is we'll go through and write some of these things so we could do with adding the validation middleware so in middleware if we add a new file validation dot middleware dot yes i might make that just a little bit bigger and so you can see the name the file names and inside of here we're going to import some master from express request response next function and request handler from express and then oh we need to install something actually once again so just one more thing to install and i think this is the last thing to install in the entire entire video so that's good um mpm install joy which is um a validation library so let's have a look import joy from joy and we'll do function validation middleware and we'll do the schema and that's going to be of type joy dot schema and the return is request handler so we'll do request handler and open curly braces will return async rec it's gonna be request res response and then next next function okay i'm just going to return a promise it's going to be void and using arrow functions we'll do um const validation options so i'll try and go through these options the best i can so the first one is about early um that's gonna be set to false and abort early um what the validation middleware will do is go through um all the data that's been passed through and validate it with the validation options that we pass through um essentially the schema up here so when we pass through this schema it'll validate against the schema and a bar early will basically make it so that on the first validation failure it will stop the request and return back to the user all of the errors but if we don't do a bar early it will keep going and then find any more errors it can and i think that's more useful for the end user so for example if we had like a login form that used like username and password um instead of just failing on using him let's say they didn't provide a username and it failed on that and then said you need to add a username let's say they also didn't pass through a password they wouldn't know that or like they did pass through a password but it wasn't valid for example um it would tell them that but if you if you aborted early it wouldn't tell them but if you didn't it would add that to the list of errors and then tell the end user oh you also didn't you know type in the password correctly for example um so i think that's more useful um but yeah it depends on your situation i suppose allow unknown so for some reason i can't spell that but a lot unknown is going to be true and this will allow values that um and as aren't part of the schema so it won't fail if something's passed through that isn't part of the schema um that's because we we don't want it to fail pointlessly like that that seems a bit pointless uh a pointless way to fail um so what we can do actually to sort of mitigate that is do strip unknowns so we're sort of allowing them to send to submit um data that isn't part of our schema but then we're also stripping it out and get rid of it because we don't need it so this will stop it from crashing if something unknown um appears and this will get rid of the unknown stuff um so i think they work quite nicely together and then inside of a try catch we're gonna do const value equals away schema dot validate async rec dot body and then validate options and then we're going to set rec dot body to value and essentially um value is going to be equal to the data that's passed through after being compared to the schema and um applying these options so it may it'll strip all the unknown data and validate that the data is all valid um and then assign it to rec.body and then call next so that we can continue with the request or what might happen is if it fails and we'll change this to an e um we can create a const of errors and that's going to be an array of strings and we'll just um set it to an empty array for now and we'll do e dot details which is where all the errors would be stole at starred and then we'll call for each um error which is going to be equal to joy dot validation error item and then we're gonna have a function that's just going to gonna um push the errors the error.message into the errors arrear i'm gonna make arizo.push that should be fine i spelled it wrong that's why it's um kicking up a fuss as usual um res don't stairs and we'll just set it to 400 because something's gone wrong and what we'll do is we'll send through the errors so we can display them on um either it could be through an api request um you know from a third party that's using our api and it says oh you can't do that or in like um a react application for example i don't think we need these errors this error has call on errors i think we can just call it errors like that and it'll automatically set it to errors but i'll um i'll just leave it like that for now and then let's export this export default validation middleware and that should give us our validation middleware um there's two slashes i don't know why so now what we need to do is add validate and what this is going to be is the specific schema for our post stuff so if we go into the validation file and we do import joy from joy we'll do const create oh const create equals joy dot object and then inside of this object we're going to expect him to see a title so we'll do joy dot string dot required and we're also going to want to see a body so joy dot it's gonna be basically the same thing string dot required uh required and we're gonna export default and what we're gonna do is export an object with this create init because the idea of this file is that it contains all of the validation we'll need to perform actions on our post resource so for example we've got create but we could have update for example and that's also going to essentially request the same stuff but we'll do update and then inside of our controller when we import this we can import create an update at the same time and we just do validate.create and validate.update depending on what we're what action we're performing for now we just need to create so we can get rid of it with it but we'll keep it like that and to make it easier in the future when we add stuff we'll know what we're doing um and now we should have that available so we can actually start writing some code here in our controller um for the actual api functionality so what we'll do is we'll do this dot router so we're going to add a route to this router we defined up here and it's going to be a post route and first thing i'm going to do is add the path and because we're we're sort of following rest api um our restful what would you call it sort of paradigm um basically it we're making a rest api um you can do whatever you want but um in a rest api when you want to create a resource you do so for example ours is called post is a post resource so if we want to create a resource we'd go we send a post request to slash posts to create one so that's what we're going to do here essentially so we can do this dot path so that'll set the path for the um route that we're creating to uh slash posts and because this is added to the router inside of here as well it'll also have slash api um in front of it so essentially all of the paths that we defined in here will have slash api in front of them so good just good to bear that in mind um in case you're struggling to send a request to your api validation middleware so this is where we can finally use um our validate stuff so create and then the final thing is going to be the actual um request handler we want to call as part of this request and in our case it's going to be create we haven't um actually added this function yet so we'll do private create and this is going to be equal to async and inside of here we've got rec res next and it's going to return a promise of either response or void there we go and we will do we'll do an arrow function and inside of here we'll do a try catch um because we're doing async await code we do try catches um to catch any errors so what we'll do is we need to get the title and the body from um rec.body so if we do another d structure on the object so we do title body and that's going to be equal to wreck dot body so essentially this creates two um const one for title and one for body so we can use them separately later um we haven't actually created post service yet so you just follow along the code but we'll create this a little bit later so const post equals and it's going to be equal to await this dot post service dot create and i'm gonna pass in title and body to this and what this will do is um we'll create this post service that has all the functionality we need um for posts so like create update delete that kind of thing and um what we'll do is we'll use them instead of writing the logic directly into the um the handler the request handler will use it we use a service and that provides some advantages um let's just add this first so we'll return the status 201 to indicate that we've created an object and then we'll return some json and it's going to be post so we'll return the post which has been created so using a service is useful um mostly and this is what i found mostly because if you want to like create a post somewhere else in your code it's nice to have it inside of a service and not um instead of a request handler because a request handler is very specific to express and you may want to create a post as part of something else um so post isn't probably probably the best example let's say emails for example if you wanted to send an email you may want to send an email as part of a queue which is something we're going to we're going to look at later on in the series um and basically you don't want to be sending a request to your own api it would be better just to call the key the email service and the and use the send function that's in that service as opposed to you know having an end point that sends emails that's much easier and you can use it as part of the queue um so it probably doesn't make too much sense as to why that's useful now but it is very useful to have that solve everything inside of this poor service so we can use it anywhere inside the code so now we're going to do is just return an error if there is an error we'll just return an exception sorry a http exception 400 if i can see the number um oh god cannot the game is getting worse cannot create post i think for my next video i need to um look into the position of my mic so what this will do is it'll send this http exception through to our error handler which is up here and as you can see here it's expecting an error of type http exception and then it will use the status and the message we pass through which is here 400 and cannot create post and send that to the user so that's quite useful um the last thing we need is this post service which can be quite simple to do um we just need to import the post model um and that's another thing actually about using services is that any of the models that we use we we import them into the service and not into the controller so it sort of keeps the controller um as least cluttered as possible which is quite nice um i know when i've been doing stuff i haven't used services and my controller becomes a mess so having the services just splits things up a little bit which is quite nice i'm going to import that from at resources slash post slash post model and then we also want to import post which is going to be a bit confusing um this is basically going to be the interface um some people when they do interfaces they call them like ipost you know that i being interface um you can do that if you want i i've never done that i've sort of just based it on i guess the context it's in so i can tell that that's an interface quite easily by how i use it and stuff but if you find it confusing um feel free to call it like ipus i think i've read about people sort of discouraging that not 100 sure why you know when it comes down to um sort of the format that you use for your code it's not really about what you do it's it's more about consistency so as long as your code is laid out and then formatted the same way throughout your code base that's usually fine so if you do i post for example there's no issue with that as long as you always do i post when you're importing interfaces and and naming them so just something to bear in mind um i'm gonna do a private method here again this is something that you don't have to do um and i'm going to set it confusing again i'm going to call it post and post model now you don't have to do this because you can just use the post model directly basically this just means i have to do this this dot post dot create for me that feels nicer than writing um post model.create it's up to you how you do that um you can do however you want so for this i'm gonna follow some good practices and create a comment um and i'm just going to say create a new post it's quite nice to have this because then you you know what the functions are doing i mean some of them are obvious like we're creating a create function so it's quite obvious what that does but you may also want to write about the and you don't really need to do this with typescript um i was going to say you could write about the parameters like the type the type of the parameters and the parameters it needs um but with typescript it's already included in the function definition so you don't really need to do that but just an idea you kind of there's other stuff that you can do in comments that'll make things nicer when you're writing code um so we're going to create a public function it's going to be async create and it's going to accept title which is going to be a string and a body which again string and it's going to return promise and it's going to be of type post so that's why we um included this interface here it's saying the values never read oh no there we go yeah um so basically the warning here is that we haven't um actually returned anything in this function even though we're meant to be returning post so what we'll do is do another try catch apologies um const post equals await this dot post dot create and we're gonna pass in title and body and then that will return post yes it'll assign um the result of this to post and then we'll return it back to our controller otherwise we can throw an error and pass through some text enable to create post now this is really up to you again and i might advise doing this um so basically our post service is done now um you can add new methods as and when you need them so we only need create at the moment but you could add update or delete or something like that in your controller um we need to do this private post service so it's actually the same but the thing with the post service is because it's a class we actually need to initialize it so i'm going to initialize it here again um you can probably do this in the constructor for example um but i'm just going to do it here might as well so that's pretty much everything done and the thing that i was going to say that you might want to do is in in here in our service we're throwing an error if it goes wrong and we're adding an error message and you can use this error message inside of controller so instead of doing cannot create post you could do error dot message and just rely on the message you send from your um your service the function in your service so if i set that to error.message it would pass through unable to create post as opposed to using cannot create posts now that's it's up to you i would recommend doing um the other way because then you can get some more fine-tuned fine-tuned tuned error messages potentially um but if you want to do it like this and just do it like that it's fine um so this is everything that we need for our api as far as i'm aware um oh we do actually no no not fully not fully there is something we need to do and this is important and i'm glad i remembered it because it's like the most important thing um this is a post controller uh yep so export that and we need to use it here so we do new post control now here's the thing it makes it a bit easier to actually know that's a bit annoying okay the the problem i think is that does that work yeah okay never mind yes he was doing the relative path um basically what i did there was just i started typing post controller and it recognized that i'm probably gonna i probably want to include this file here because it recognizes it it's got the same name but when it imparted it it didn't use the at which is what i'm trying to do across my code base just to keep everything the same in fact what i might do as well is just move this app up a little bit just to make it a bit cleaner i think that looks nicer um but yeah it was using the dot so yeah just use this ah again i mean it's up to you how you do things but um yeah i'm gonna use it and basically what we want to do is instantiate the post controller so that it sets up the roots and what this will do is it'll pass it into uh your constructor here controllers and then pass that into initialize controllers and then it'll loop through your controllers and set them up using your router which is going to be in here this here and then roots get added to this router through initialize routes and that all gets added to the main express router and then it makes it so you can access these routes hopefully that's not too confusing um you can look through the code and sort of get to get to know how it all works hopefully i explained it somewhat well but any issues let me know in the comments and i'll get to them hopefully i can answer them and i can clear stuff it up in later videos as well if needs be and what we'll do is we'll run this code um and just give it a try so we do npm run dev what it should do is it should build our project and it should start in compilation mode as you can see here and it found an error object is a type unknown this is in validation middleware.ts 2025. foundation middleware.ts 25. all right so i managed to fix the issue thanks to uh stack overflow um apparently typescript 4 which is weird because i didn't have any issues on my other code base but um yeah typescript 4 this is basically the solution um passing i'll do that use um any as the type on this um it doesn't seem to cause problems anywhere else it's just for some reason here it's causing an issue so yeah just for now do call on any and i can revisit this if needs be this there might be something more specific i can use but for now using n is fine um because we know how this is going to work so we can sort of ensure that um it's going to pass through what we want it to pass through anyway um so yeah just this call on any to solve this and you can see here it's listening and it found no issues one thing we can do we can test it i'm just going to change some code in the other um browser um in my other screen and you can see here that it redid the compilation it's doing incremental complicate compilation sorry which is faster because it doesn't compile everything so we can test this now if we open up um insomnia i use insomnia which is a rest client you might be more familiar with postman use whatever you want to use i like using insomnia just because of how simple it is so we're going to try and do a post and we'll hope that this works so if we do post there we go 201 created and we can see here it logged post api 201 and even told us how long it took so this should now be in our database if i opened up mongodb compass and i should see it inside of here so i should have probably opened up this before the tutorial but uh there we go um there we go and localhost just connect to this and here we are posts and here we are so we can do other stuff with this obviously and you can use this as the base for your own api and you can basically this has everything it needs to get going um what i want to cover in the next tutorial is authentication obviously quite important topic when it comes to creating apis so i want to cover that in the next tutorial and then after that it's really anything you can think of because like i said the way it's set up is um you will need to have really well maybe sometimes but for the most part you won't need to have watched any of the previous tutorials besides this one and even then i'm going to upload all the code to um github and i'm going to tag it as well so i'll tag this as like the initial version um the base framework and you know my future tutorials could be based off of the base framework and not like stuff that i've done you know between those tutorials for example so they'll all be sort of independent in some way which makes it easier to sort of get in and see learn something that you want to learn because maybe i'd cover stuff that you already know and you don't want to watch the tutorial but you want to you want to know some of the other stuff like maybe the queue system that's going to be something i'm going to do soon um but you might not want to watch you know authentication or anything like that so that's fine any issues any requests um of topics you want me to cover let me know in the comments um if you enjoyed the video leave a like and subscribe for more content and i will see you guys next time
Info
Channel: Rettson
Views: 961
Rating: undefined out of 5
Keywords: Create A NodeJS API From Scratch Using TypeScript And MongoDB, Create A NodeJS API Using TypeScript, Create A NodeJS API From Scratch, How To Create An API With Nodejs And TypeScript, How To Create A Rest API With NodeJS, How To Create An API Using TypeScript, How To Create An API Using Node JS, How Do I Create An API Using NodeJS, mongodb, rest api, node.js, Introduction To NodeJS, typescript, node js, express js, javascript, node.js api from scratch using typescript and mongodb, node
Id: 1o9YOHeKhNQ
Channel Id: undefined
Length: 91min 1sec (5461 seconds)
Published: Wed Nov 17 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.