Express JS with TypeScript - Setup, Examples, Testing

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone this is ansen we're back with a brand new video a lot of you have seen my expressjs tutorial that I uploaded about two months ago it covers all of the core fundamentals that teaches you how to get started with expressjs from building simple API endpoints all the way to writing integration tests now a lot of you actually asked for that same tutorial but with typescript now I am going to show you how to set up an expressjs project with typescript but I am not going to remake that whole tutorial with typescript because I think there's a lot of overlap and there really isn't too much of a difference I think the biggest portion of it is just setting up the project in typescript for Express so I'm going to try to keep this video as short as possible but also as informative as possible showing you how to set up your project from the ground up using typescript for expressjs and then as you go along the video I will show you examples of how to do type annotation for your functions for your arguments objects whatever it is and then you can start to piece everything together so that way you'll build an intuition on how to convert your entire expressjs project into typescript so again this is not going to be the same 8 Hour tutorial just done in typ script I'm just going to show you all the relevant things that you will need to do so that way you are prepared and you'll know how to convert everything over so let's go ahead and get started what I'm going to do is I'll set up a project right now I'm going to call this expressjs typescript uh typescript example tutorial I know that's a long name but we'll just leave it like that for now I'll CD into that folder and what I want to do is I want to set up a package.json file so we're going to type npm in it hyphen Y and then we're going to install our dependencies so there's a few dependencies that we will need to install so I'm going to first install expressjs so npmi Express so we just have it installed and now the rest of our dependencies that we will be installing are just going to be developer dependenc so the important one that we need is typescript because obviously we're trying to set up a typescript project so we need to make sure we install typescript into this project so what we want to do is we want to type npmi but we want to install typescript as a Dev dependency because typescript is supposed to be used for development only so when you actually deploy your application to a production server you don't want to actually run typescript on the production server you want to actually run transpiled typescript into JavaScript on the server okay so we'll install typescript as a Dev dependency so we're going to type npmi hyphen D which means Dev dependency typescript so this one saw typescript as a developer dependency I'm going to open up visual studio code and we'll see our dependencies are listed over here in its own uh category so we have dependencies and we have Dev dependencies right over here and if I look in the node modules folder you'll see that if I go into this bin folder you should see this TSC uh this is a binary this is an executable and this is going to be the TSC script that we're going to be using to do things such as building our project to transpile all the types of code into JavaScript code and other things okay so now before I do anything else I want to show you how we would actually write our expressjs project in a typescript file so we're going to rightclick and create a new folder and I'll call it source and then I'm going to create a new file instead of naming it index.js we're going to name it index.ts so index.ts just like that and now what I'm going to do is I'm going to import Express from Express and I'm going to go ahead and create my Express app instance the same way that I would do it in JavaScript so const app equals Express and then call this function and then now I'll just declare a variable for the port we'll assign it to Port 3000 and then now what I'm going to do is I'm going to call app. listen now you'll notice that it's not picking up the listen method in intellisense if I actually hover over this app variable you'll see that the data type is any so typescript actually does something called type type acquisition and we need the appropriate type files or the type definitions for our Express package and that's also another reason why we don't have this method appearing after we try to reference the app variable because we need the type definitions for Express and most packages will have these type definitions you just need to install it so what we'll do is we'll go back into our Powershell and I'll type npmi and then we'll use the hyen D flag because this is going to be a Dev dependency as well and then we're going to do add types and keep in mind that all of your type definition packages that you will install will pre be prefixed with at types okay it's pretty much the standard for your for the uh npm registry so at types slash and then the name of the package just like this okay there we go so now we've installed that and all of your type definitions will actually be located in the node modules folder inside this at types folder and you'll see that it did install not only the ones for Express but for other for a few others as well okay we have Express right over here and look what happens now when I hover over app you'll see that it is no longer of type any but of type Express so if I try to do app. listen it'll actually pop up under intellisense and it'll show other methods as well let's go ahead and pass into the port and then a call back and we'll do console log running on Port and then ports so again everything that I've done so far is no different than how I would set up a simple expressjs project using just plain JavaScript in an esm module or ES module so one more thing that I will mention is sometimes your typ Ty Acquisitions may not reflect automatically so if you hover over app and it's still of type any what you can do is you can go to your command pallet so if you just go to view and then select command pallet or just hit control shift p on your keyboard and then over here you want to type restart TS server and for my case I already have it over here because I recently used it so I can just click on that and it will restart the typescript server for you and you want to do this often times the TS server may be lagging and it's not automatically reflecting your configuration or your types in this case so you definitely want to do that now right now if I wanted to run this index.ts file how would I do that you're probably wondering well couldn't I just use the node command so let's just type node and pass in the index.ts relative path and you'll see that we immediately get an error and it's complaining because we're trying to load an es module okay by default typescript uses es modules and we're trying to use the node command which assumes every single file that you're trying to run is going to be commonjs unless if you set the type to module in the package.json file we don't obviously want to use commonjs with typescript that wouldn't really make any sense and I don't even think that would work so what we want to do in order to actually run our code well there's actually two ways that we can do this okay what I'll do is I'll show you how we can actually transpile everything from typescript to JavaScript and then use the node command to run the actual JavaScript files and then I'll show you how we can use node TS which is pretty much just a node interpreter but for typescript so in order for us to actually transpile our code we need a tsconfig dojon file so we don't currently have one but we can easily generate one so remember how earlier we installed typescript and then I showed you in this bin folder there's this tsse command which is pretty much the typescript compiler which is what it stands for so we want to run this TSC command and to do that what we can do is we can type npx TSC and this will basically use the local installed TSC binary that you have in your node modules folder over here if you have TSC or the typescript compiler installed globally then you don't need to do this but I would recommend avoid installing typescript globally because there might be issues with versioning sometimes so it's just better to use typescript locally so make sure you type npx TSC and then hyphen hyphen init and this will generate a TS config.js file now when you click on this file there's going to be a lot of different configuration options it's going to seem very overwhelming don't worry I'm going to focus on the ones that are going to be very important okay so the ones that are extremely important are not commented out so for example Target it's currently set to es 2016 so that's the version of the um JavaScript language you can set it to whatever you want we'll just leave it alone and set it to es 2016 and then you'll see that there's module so again this entire TSC config.js file is everything to do with how you want typescript compiler to behave when it comes to transpiling your code from typescript to JavaScript so you might see over here it says module commonjs and you're just pretty much telling the compiler to transpile everything to commonjs you could change it to uh something else you could have it transpiled to es6 or ES next it's up to you but we'll leave it as commonjs for now there is also let's see what else is there there's also uh these other uncommented ones we're not going to focus on that but the one that I want to focus on is is this so we want to look for this this root D which is currently commented out we're going to uncomment it and we're going to set the root directory to the source folder okay because all of our typescript source code is going to belong inside the source folder so we want to set it to that okay we also want to set the outo property so we want to search for that and it's also currently com it out so you want to uncomment it and this out dur is where all of your transpiled code so all of your JavaScript code is going to be generated too so you want to name it something that is going to make sense most oftentimes it'll be named something like disc or build you can name it whatever you want as long as you can recognize it I'm just going to name it disc okay and so pretty much when we build our source code our typescript source code it's going to uh transpile all that typescript code and then place it inside this autogenerated disc folder okay so hopefully that makes sense there's a couple other things too that we can uncomment so for example when you're using typescript there's some compiler options that you can set to have it do certain checks for you and you know when you use typescript there's a lot of important reasons on why to use typescript one of them is because you want to have more strict enforcement of of writing code so for example you want to make sure that your data types are properly typed so we want to make sure we have no implicit any set to True we'll uncomment that out and we'll also uncomment out strict null checks so this will ensure that if you're passing something that could possibly be null it won't allow you to compile your typescript code at all we can do the same thing for strict function types and a bunch of other stuff but we'll just leave it like this for now we'll keep it nice and simple so this is all we really need to worry about for now so what I'll do is I'll close this TSC config.js file and now let's go ahead and actually try to transpile our index.ts file so in order to actually transpile everything we want to basically build out this project over here so what we can do is we can type npx TX TSC and then hyphen hyen build and it's going to use all of the options that we have in our tsconfig file so when I hit enter you're going to see that the disc folder was just generated and you're going to see inside this folder we have this index file but now it's named index.js instead of index.ts because it's transpiled down from typescript to JavaScript and you'll notice that there is a huge difference between these two files right like there's a lot more lines of code and um you can see a lot of the stuff that you see in the transpiled code is a lot of these things are code that you would never actually really write in even in a JavaScript project as well okay so you can kind of see how cool all this stuff is but now what I can do is I can actually use the node command to run not the index.ts file but the index.js file so all we need to do is just type node and then pass in the path the relative path to to index.js so that's going to be slist or whatever you name that outdoor folder and then index.js just like this and you can see that uh let's see it's saying that it's currently in use which it shouldn't be let me fix that real quick okay let me run the command again I just had to terminate some of the node process that we're running in the background and there you go now it's running on Port 3000 so we can go to our browser and then we can see that over here it does work and it's saying cannot get which is fine okay wonderful so it's working all right so obviously I don't want to have to keep on you know manually typing out this command all the time so let's set up some scripts which is very very important so let's set up a build script first which is very common in every single types script project and it's basically just going to be an alias for this uh TSC hyphen Ty build command that I just set up so what I'm going to do is I'm going to type TSC and we don't need npx anymore because what happens is inside your patch. Json file when we run the build command it's going to go ahead and look inside the node modules folder and it's going to basically just execute this uh uh tsse binary so we don't need npx to do that okay so we're just going to type TSC and then hyeny build and then that's pretty much it there also are a bunch of other options you could pass into the build script or the TSC command if you wanted to I recommend you look that up in the typescript documentation there's a lot so definitely check that out if you want to and let's see we have a build script now let's also set up that start script so we'll set up a start script and this is just going to be an alias for node and then it's going to run the index.js file so let's do that node and then slist index.js just like that now I'm going to show you how we can actually have our project our typescript project set up so that way we can use nodemon which many of you are definitely familiar with noemon it basically will detect file changes and restart the process so you don't have to manually restart it over and over and over again okay so we're going to use nodemon so what we'll do is we'll go into our terminal and I'll install nodemon as a Dev dependency so npmi hyphen D nodon and now what I'm going to do is I'm going to set up a Dev script so I'll call it start colon Dev you can just call it Dev if you want to many people just leave it like Dev but I'll just call it start colon Dev and then we're going to have this be an alias for nodemon and then this time we're going to pass in the path to our index.ts file okay so let's try to run that script so npm Run start col inev and you'll see that it's going to throw an error now you'll see that it says TS node is not recognized as an internal or external command and the reason why this is happening is because nodemon for a typescript project needs TS node and TS node is basically just a nodejs interpreter just for typescript so we're going to want to install TS node so let's type npmi hyph and D TS node so we're installing it as a Dev dependency and again you can see inside the node modules folder inside the bin folder you'll see that nodemon is installed right over here that's the binary that's the executable and then we have TS node over here so now what I can do is I can just rerun that script that Dev script and node mod will actually work because it's going to see that it's trying to run a TS file so it's going to use a TS node interpreter okay you can also just use the TS node interpreter so let's type npx TS hyphen node and then you can also just uh run that typescript file like this but of course this is not going to detect file changes so you will need to manually restart your application whenever you make changes but there's no real reason for you to do this and TS node does not have options for watch options so that's why we need to use nodemon for that okay so again hopefully all of this stuff with the scripts makes sense I'm not going to do the test script right now I will do that later on towards the end of this video where I show you how to set up just with typescript for your Express project so stay tuned for that but let's go ahead and exit out of this process and let's move on so we have three major scripts that are set up so again for development we're going to use this start colon Dev script so that way as I am making ch changes to my code so let's start this up again if I just do this app.get you'll see that it will restart for me automatically and I don't have to exit the process and start it again which is pretty tedious and annoying we also have our build script so this is important because before you actually deploy your application you need to make sure that you are building your whole project which means you're transpiling all of your typescript source code into JavaScript code and then on the deployment server you're going to go ahead and run this start script which will just basically use the node command to run your index.js file because that's the entry point to your application and that is how your production server or your deployment server is going to serve that application to your clients okay so these two commands are very important as well for any deployment server that you're going to deploy to so now what I want to do is show you some examples of how we can type annotate our functions and arguments in expressjs so let's say I want to set up a get request so I'll reference the app variable and I'll call app.get and I'll set up a simple path so/ API users to get all of the users we'll need our request response Handler which is going to be a callback function and then our callback function remember is going to need the the request argument and the response argument and then sometimes you might need to work with the next function so I'll just add it in just so that we cover every single case now right over here I can go inside the Callback functions body and I can reference request and you can see that intellisense will pick up all the properties all the methods and I can do whatever I want okay I can reference request. query to access all the query parameters request. prams to reference all the rout parameters nothing different than a regular JavaScript project same thing with the response object I can call response. send and I can send back an empty array and do whatever I want okay now in this situation I don't necessarily need to type annotate the request argument or the response argument or the next argument because typescript remember does something called type acquisition and it's going to map the appropriate type to these arguments so if I hover over here you'll see that the type annotation over here uh is this request and this is actually an interface okay and you can read a little bit more about this it it can be confusing sometimes but this request is in fact a generic type same thing with this response over here okay I can actually right click and then click on go to type definition and you can see that it takes me to this index. D.S file remember this is a declaration file and then we have this request which is an interface and there's a lot of stuff that's going on here it is going to look very confusing sometimes again you don't have to worry so much about reading through all this but just from scrolling through you can see that okay I can see that it has all these methods uh I can see it has uh let's see these properties um again there's a lot of stuff it might be confusing don't worry so much about it okay now in terms of type annotating your arguments what I could do is I could type annotate this with the request and you want to make sure that when you do type annotate type annotate it with the request interface that you're doing it with the interface that comes from the Express package because right over here intellisense is picking up this request interface but this is actually not the correct request interface this first one that's right over here if I try to go to the definition and if I click over here you'll see that this request interface is actually coming from uh this uh node global. D.S package and this is not the correct request interface and if I actually left it like this it's going to actually complain about how uh this argument is not the correct argument itself okay it's not the correct type so what you want to make sure you do is you type annotate with the express interface over here so when you click over here whoops let me do that again when you click here it will import it from the Express package okay and then now you can see that the error goes away same thing with the response object when you want to type annotate you want to make sure that it is coming from the Express package so just like that okay and then we'll do the same thing for the next function so the type annotation will literally just be next function and then you can always rightclick each one of these import and then click on go to definition and it will take you to the Express package for the types and it'll take you to the Declaration file and it'll show you the interface itself so now I'm going to show you an example where we can set up a more complex file structure with our Express project because not all the time you'll want to have all of your API routes defined and one single file in our case index.ts so what I'll do is I'll use a router to set up a user's router and then I'll show you how we can create a proper file structure so I'm going to go into the source folder I'll create a new folder called router or routes and I'll create a new file called users. Cs and again this should be very similar if you've learned about expressjs and you've learned about routers in expressjs so this shouldn't be anything new we're going to go ahead and import the router from Express we're going to create an instance of the router by calling that router function just like this and then I'll export the router as a default export and I'll go ahead and set up a route with the router and then we'll need a request Handler but instead of just passing in an anonymous function just like this we're going to actually pass in a named function that is going to be uh imported from a different file that will create so I'm going to remove this before I do anything else I'm going to go into my index.ts file I'm going to import that users router from routes users just like this and I want to register this users router so I'm going to go down over here after where I have initialized my Express app and I'm going to go ahead and call app.use and the first argument is going to be the path prefix okay so the prefix is going to be SL API users it could be whatever you want but I'm going to do SL API users and basically every single route that is going to be registered in my users router will be prefixed with SL API users so for example this router. getet path this is just a for/ path but in order to actually access this route you need to actually go to SL API SL users just like this so if I wanted to access let's say router. gets let's say uh let's say we have a route parameter of ID so let's say we want to get a single user by its ID then the actual API would be SL API users and let's just say 1 2 3 okay so hopefully this makes sense again you should know this already if you've worked with routers but I just want to mention it just in case some of you uh are not familiar with this so I need to pass the users router as an argument the second argument to apile use so I've registered my router right over here and now what I need to do is I need my route handlers okay so we're going to create a new folder and I'm going to go ahead and call it handlers and I'm going to go ahead and create a new file and I'll call it users. TS personally for me I like to to map or match the file name for handlers with the route so that way I know which file contains what handlers for which router you don't have to do it this way but this is something that is common I personally like to do it because it helps me recognize each file and where everything belongs so now what I'm going to do is I'm going to create a Handler function one for API users and one for API users slid so we'll create the function and we'll also export it I probably don't need async right now but in case if I didn't need it I would add the keyword in front of it but we'll just do export function and I'm going to go ahead and give this function a name so I'll call it get users and then I'll pass in the arguments and the arguments are going to be the same exact one that you would pass in for your Anonymous function okay so remember we have our request and our response arguments and if you needed the third argument the next function you would pass that in so I'm going to go ahead and do that real quick so request response and we don't need the next function so I'm going to Omit that so now you'll see right over here if I were to try to reference request you'll see intellisense doesn't pop up it doesn't give me any of the properties that I can select from okay and in fact if I try to reference it like this um nothing would actually happen but you'll see that we have these uh errors over here it's saying parameter request implicitly has an any type so what this means is that you need to make sure you are properly type annotating your arguments and the reason why this error is happening is because in our TS config.js file we have this no implicit any set to true and you don't need to have this set to true if you don't want to but I recommend you do because again you most likely do not want to use the any type because personally if you're going to use typescript there's no reason for you to use the any type sometimes in very specific circumstances you might need to use it but probably like 95% of the times you don't want to use the any type if you can get away without using the N type then you should but again it kind of defeats the whole purpose of using typescript if you're just going to type annotate everything with the any type okay because what that's doing is it's setting the type to literally any and you don't have proper type annotation so I could set it to any and this error would go away but that's kind of like not really um using typescript to typescript way if that makes any sense so what we want to do is this first I'll show you how to disable disable this so all you have to do is just go over here no impli any and set this to false and then now you'll see that these errors should go away however if you hover over it it will still show you this uh it's not really like an error warning but it's just going to show you that this is not it's not necessarily a problem like your application still will build and it'll run but it's just going to note that for you in case if you wanted to do something with it okay but what I'll do here is I'm going to properly type annotate these arguments the same way that I did in the first example in our index.ts file so we're going to go ahead and use colon and then we're going to import request from Express just like this and then we'll do the same thing for response so we'll import response from Express remember these are interfaces that are being imported from the Express package so now we have proper type annotations and I can reference request and I have all of the properties all of the um methods if I needed to call anything okay pretty straightforward stuff and then same thing with the response I can go ahead and do response. send and we'll just send back an array okay so hopefully this makes sense so now what I can do is I can go back to my user. CS file in the routes folder and I can pass this get users function as an argument to this router. get call so as a second argument I'm going to go ahead and pass get users and I'm going to import that and that's going to be imported from the handlers users. TS file up over here and we're passing that as a callback function over here and again this is no different if you were to literally just copy this whole thing and uh pass it in as an anonymous function right over here uh let me see if I can clean this up a little bit let's import request and let's import response right and you can see there's no difference over here here the only difference in this situation is that you're Now using an anonymous function and not a named function and the downside to that is let's say for example if you wanted to let's say reuse this function if you needed to then you would not be able to kind of like extract that function from this .ts file from the routes folder you would have to basically redefine the entire function and reimplement the entire logic and also it would make it a lot more difficult testing because how are you going to test an anonymous function because you can't import a function that doesn't have a name okay so again I recommend you avoid using Anonymous functions because it just will cause a lot of headaches down the road okay and it makes it a lot easier for testing which again I'll will show you how to do later on so definitely do this approach where you create the actual function itself type annotate it and then import it and pass it as an argument okay so hopefully this makes sense let's go ahead and quickly do the same thing for our second router. getet so let's go inside our handlers users. TS file and I'll just go ahead and copy this function and I'll just rename it to get user by ID and uh I'll just leave the logic alone for now and I'll just import get users or get user by ID pass that as an argument and and I'll just actually send an empty object instead of an array for now okay so now let's just test everything out remember we have already registered our user router so let's go ahead and run our application and test everything out so npm Run start colon Dev okay and let's make sure everything is good no errors so now what I'll do inside my browser I go to Local Host Port 3000 and then we'll do/ API users and you'll see we get back our empty array if I try to pass a route parameter so let's just do one it's going to give me back that empty object so we know that both endpoints are working fine so now I want to show you how we can type annotate request bodies because this is going to be a common thing where you have let's say a post request set up where you want to send data from the client to the server and then you also want to make sure that when you reference the request body it has the proper type annotations so what I'll do first is I will go ahead and set up a post request so let's go over here and we'll do SL API users and then we'll do router. poost so this will be for creating a user and we'll just leave it like this and then now we'll just need to create the request Handler so I'll go down over here and I'll call this function create user and I'll make go ahead and just quickly import that in the user. TS routes in in the routes folder for users. TS create user okay and let's go over here and let's do request request response response okay so let's say for example if I needed to reference requestbody you can see over here if I hover over it's going to show me that this is the current type annotation and this is a lot of stuff to understand but ultimately what you need to know is that if I try to reference a specific property I don't get Intelligence on that so let's say for example I expect that the request body is going to have a username email and password field okay but intellisense doesn't show any of that and typescript doesn't know that these fields exist on the request body so how do I actually get it to work where the fields are showing up up on the request. body object so couple things that I want to mention first is you also want to make sure that you are validating your request body first so we're going to assume that our data has been validated already and that is typically done in a middleware so again I assume that many of you have seen my expressjs full course where I go over validation if you haven't seen that already go check it out it should be pretty straightforward again middleware is not any different in typescript versus JavaScript so check that out but we're going to make the assumption that our data is already validated okay so what I'm going to do is I'll create a new folder and I'll call it uh let's do dtos okay so dto stand for data transfer object or dto stands for data transfer object and what dto represent is basically a way for you to represent data that is being transferred from one party to another in this situation we are technically you know transferring data from the client to the server or you could transfer from server to server whatever it is okay so we use a dto to represent that and all a dto is is just either a class an interface whatever doesn't really matter so we're going to go ahead and create a new file and I'll call it let's do create user dot uh or let's do dto dots and again you don't have to to do this I personally uh like keeping everything organized so that way I know where everything is what and I know what is what so what I'll do here is I'll actually use an interface so I'll do export class create or whoops not class interface create user etto and this is where I want to specify each field so I'll go ahead and do username string email string and then password Str string okay and you'll notice how I am not marking these properties as optional like this because again we're making the assumption that the data has already been validated already so if I actually rightclick this request interface and go to type definition you'll see that if we look over here we have our interface and you'll notice that there are these angle brackets right after the name of the interface this request interface so this is known as a generic okay and I highly recommend you understand generics if you don't already because you're going to be working with them a lot in typescript or really with any um programming language that has uh objectoriented features such as generics it's not specific to typescript okay so if you already know generics that's great if you don't definitely study that but if you look right over here where this wreck body is this is the third uh I guess you can we can call it a parameter a type parameter okay and basically what we want to do is for the third parameter we want to set our request body type So currently it set to any but we want to change that so what I'll do is for this request over here I'm going to go ahead and provide a pair of angle brackets and for the first two I'm going to pass in empty objects but for the third one I'm going to pass in this create user dto interface that I just defined so create user dto and that's going to be imported from our dcre user. dto file and now what I can do is I can reference requestbody and if I just reference by using the dot operator you'll see that intellisense or typescript now knows that these three Fields exist on the request. body property so I can reference all three of these fields however I want and I can do whatever I want with them okay so hopefully that makes sense if I were to remove this and then just leave it as an empty object you'll see that typescript thinks that this request. body property is of type any and it's not going to recognize any of the actual Properties or not of type any but of type empty object okay so again hopefully this makes sense and hopefully you now know how to properly type annotate the request body and again you could do the same thing for query parameters too so if I wanted to like let's say type annotate query parameters so right now you can see that when I reference request. query and if I try to reference uh the query object nothing shows up so to do that again I can look over here in the generic request type and I can see over here that it seems like uh the for query parameters it's going to be the fourth uh the fourth type parameter so right over here so what I'll do is this I will go ahead into I guess uh we'll do it inside DT's or you know what I'll do instead is I'll leave dto specifically for request bodies I'll create a new folder and I'll call it types and I'll create a new file and I'll call it a query prams TS and I'll do this export interface create user query prams just like this so just as a simple example Le let's say after we create the user we want to Auto log them in so for query parameter I'll just do something like uh login after create and I want this to be an optional quer parameter and I'll set this to be a Boolean of course when you pass in the query parameter from the client to the server it's actually going to be a string so you need to make sure you're properly uh parsing that string or that quer parameter to a Boolean value okay well again we'll just assume that it is a Boolean well we'll assume that it's parsed so let's go ahead and take this interface and remember it's the fourth typed parameter so we'll go ahead and do that real quick and I'll save and now if I reference query you'll see that this login after create property shows up in the query object itself okay and I can reference it and intelligence picks it up so that's good okay of course even if you didn't type annotate these things you wouldn't really get an error and you would be able to reference it but I would say for good practice it's worth type annotating these properties and again you can also do the same thing for the let's see you should be able to do the same thing for your route parameters I believe that's for the first type parameter we'll I'll just do a simple inline one real quick just as an example so let's say let's just do ID string and let's just see what that looks like request. prams and yep I was right request. prams ID and if I were to just leave this as an empty object you'll see the ID does not exist on request. prams so hopefully that makes sense so yeah go ahead and be creative with this play around with the other two um request handlers and try thinking of things that you would want to do for your own API so that's how we type annotate our request body our request parameters and our query parameters now sometimes you may want to have explicit return types for your response so let's say for example after we create a user we want to send back the actual user that was created of course without the password field at all so in this situation what I might want to do is I want to set the status code to 2011 and then send back the user record okay right now it's allowing me to send literally anything I want but I want to make sure that it is only sending back a user record to the client so much like the request interface I can also type annotate my response interface if I rightclick response and go to the type definition you can see that we have this response interface and it's very similar to the request interface in the sense that it is also a generic and also has typed parameters as well and you can see over here we have this res body which is currently set to any and what I could do is I can create a user record or user type and then use that as the typed parameter so that way when I call response. send it is going to expect us to send back a proper user type so what I'll do is in my types folder I will go ahead and create a new file F and I'll just call it uh I guess response TTS and typically if you have like let's say a database setup or if you're using something like um let's say you're using type RM or Prisma you can actually reuse those types that were already created based off your schema I don't have a database setup right now so I'm going to have to create this from scratch but I'll go ahead and do this I'll do export and I'll use interface and we'll do created user or I'll just I'll just do user and uh let's go ahead and do this ID number email string uh let's see username string and of course we typically would not want like a password field so I'm not going to include that we'll just leave it with this three with these three fields for now and what I'll do now is I will go into my create user function and for the this response I'm going to provide a pair of angle brackets and for the first parameter for the first type parameter I'm going to set that to user and I'm going to import that from my response. ts file in the types folder and you'll notice how the moment that I actually provided that user as a type we now have this error over here okay if I were to remove that you'll see there's no error but if I put that back there you'll see that we have the error if I hover over you'll see that argument of type empty object is not assignable to parameter of type user and you'll see that it's saying that it's missing the following properties from type user ID email and username so again it's basically telling you that the types are not the same you're trying to they're not compatible with each other you're trying to send back an object without the ID email or username and that's not okay you need to make sure you send back the proper user type with all three of those fields so what I'll do is I'll just go ahead and provide those fields we'll just hard code them as an example but of course in your situation you might grab it from a database filter out the password and then send it back so ID we'll just do ID of one username we'll do Anon and then email we'll do Anon anent dev.com and we're good to go so now what I'll do is let me also just return this response. status call and I'm going to go ahead and take my create user function and I've already passed as an argument to router. poost and we already have our user router registered so what I'll do is I'll go into Thunder client and I'll go ahead and create a new request I'll go ahead and select post and in case if you don't know what Thunder CLI is it's an extension in vs code that you can just download and it's pretty much just an HTTP uh client that you can use to make requests to your API you can use whatever you want some people prefer to use Postman I like using Thunder CLI because it's built into vs code and it has everything that you need so definitely recommend checking it out so I'm going to go ahead and make sure I've select post as my request type and I'll use Local Host Port 3000 and then SL API users and for the request body let's go ahead and uh we'll just pass an empty object because like I said we don't have any validation and we're just assuming that we're passing in valid data we're assuming that the validation has already happened if I click Send you'll see that I get back my user record so everything works great wonderful so hopefully you now know how to properly type annotate your request object with the request body quer parameters route parameters and then the same thing for the response type now before we actually go any further I do want to mention one quick thing is that the actual interface for request and response should actually not come from Express but it should actually come from Express Serve static core that's because for newer versions of Express uh these interfaces come from this package itself and in fact if you actually look uh over here earlier when we had app let's do app.get and then let's just pass in a callback function you'll see that when I hover over here and if I right click and go to the def definition oops go to type definition you'll see that it takes me to this index. D.S file but it's inside the Express Serve static core package and not in the actual Express package for the types file okay so I should have mentioned that a lot sooner but definitely make sure you have imported from Express Serve static core I don't think you might have any issues if you were to import it just from Express um but just in just to be on the safe side definitely make sure you have imported from Express uh the Express Serve static core package okay and this leads me to the next thing that I also want to mention as well is sometimes you might want to have your request argument extended with custom properties so for example many of you probably have used packages such as Express session or passport.js and one thing that you'll know is that in passportjs it has this custom user property that it assigns to the request object itself once you have uh fully authenticated with passport if you use express session there's this Express session property or session ID and you'll notice that by default it is not going to be part of the request object only when you install Express session okay so I want to show you how we can actually expand or extend the request interface so that way we can add custom properties to it it's actually really really simple so basically what we're trying to do is called declaration merging and it basically just allows us to create our own declaration file and the typescript compiler will merge those declarations that have the same Nam space so that way we can actually expand our request interface that is part of the Express package or I should actually say the express serve static core package because remember that's where the actual request and response interface that by default Express uses comes from so here's what we're going to do we're going to go into our root directory and create a new file and I'll call it index. d. TS and then what I'm going to do is this I'm going to first import uh from I'm going to import Express from Express Serve static core just like that and then what I want to do is I want to do declare I'm going to use declare keyword namespace Express just like this okay so we are reusing this namespace Express and then I can set the interface for request right over here and then I can go ahead and have all of my custom fields define so let's just say for example custom field let's just set it to string and then now if I go to my handlers if I go into any one of my handlers and if I reference request you'll see that uh let's see custom field is not popping up and the reason why that is not popping up is because oh I actually forgot that we need to set this as Global and uh let me do this should be it should be like this okay there we go now this should work so custom field and you can see custom field is showing up and all the other properties should be fine there shouldn't be any issues you can see even in our typed request interface I can still reference custom field just fine and it's a string so I can reference all these string Methods as well so that's how you can extend the request interface you're going to see a lot of different ways to do this this is just one of many ways that you can do this but it's working fine in there aren't any issues but if there are uh definitely leave a comment down below or just check stack Overflow there usually are a bunch of different ways that you can achieve this but I just wanted to mention this just in case some of you might be wondering well how do I actually extend my interface for the request so now you know and you're going also do the same thing for response if you need to but I don't think you'll ever find a situation where you will need to do that now one more thing that I do also want to show you is when you're working with thirdparty libraries a lot of times when you install those thirdparty libraries they will have typescript support built in um as long as you install the proper types package okay so let's just say for example we want to use express session which is a very popular package that many developers use to handle sessions on the server side for Express so we're going to go ahead and install that so let's do this npmi Express hyphen session okay so we just installed it and now what I want to show you is this if I went ahead and reference request you'll see that session does not appear okay and again if you have used Express session before you'll recall that whenever you use express session it comes with this session property as an object and it also comes with the session ID property but right now it's not showing up for some reason and the reason why is because we do need to make sure we install the types package for Express session because it doesn't do it by default that's pretty easy so we'll just go ahead and type npmi hyen D at types SL Express session okay and remember if we go into our node modules folder we go into types you'll see that for Express session it'll have this index. d.t file and basically what I just told you uh a few minutes ago when I talked about declaration merging and extending your uh request interface it's literally the same thing that these other packages will do when you install the types definition you'll see over here we have declare Global Nam space Express and then we have uh session store we have the interface request which is what we just did and you see now it is basically extending that request interface and it's adding this session property session ID and session store okay it's literally doing the same thing that we just did and all of these files these declaration files get merged together and now if I go back and try to reference request you'll see that we now have the session session ID and session store property I'm not going to actually set up an Express session in this tutorial but I just wanted to show you how simple it is to work with these thirdparty libraries in typescript in an Express project with typescript so hopefully this makes sense now another popular library that you will use is passportjs which if you recall when you use passport uh there's a process called serialization and deserialization so basically when you authenticate a user with passport it will serialize the user and then attach that user to the request object and it will have this user property right over here right now it doesn't exist because we don't have passport but let's say for example if you were to use passport uh with typescript I'll show you how you can have that set up well I'm not going to actually show you how to set a passport but I'm going to show you the proper packages to install so let's first install passport so ntmi passport okay and then now we need to make sure we install the types okay because if you look over here VI reference request I still don't have that user property so let's go ahead and install types so npmi hyphen D types or at types / passport and then now you'll see that we have this passport in the types at types folder and if you go to the Declaration file you'll see that we have again the same thing that we saw earlier we're extending the uh Express namespace uh we have this interface user and then we have this interface request that is being extended you can see we have user uh off info Let's see we have login um log out and again these are methods that are attached to the request object that allow you to actually um log the user in log the user out check if they're authenticated a bunch of other stuff okay so now if I were to go back to my uh Handler and if I reference request I can now see I can reference request. user and again this is a property that you would reference to check to see if the user is actually authenticated or not we have request. logout method to log the user out we have request. login uh we have request. is authenticated a bunch of methods that we saw from the Declaration file from passport or the at types passport package that we can actually use in our project so again hopefully you get the idea of how this all works okay all right so as promised the last thing that I want to show you all is setting up unit tests with just in your Express JS package with typescript so we're not going to be using just but we're going to be using tsj okay it's literally just just but for typescript it's very easy to use so what we're going to do is first install just as a Dev dependency now we already have typescript installed so we don't have to install that so npmi hyen D just now that just is installed as a Dev dependency we'll go ahead and install tsj as well and then we'll also install the types package for just okay there we go now what we'll do is we'll set up a configuration file with tsj so we'll do npx tsj because remember in our node modules folder in the bin folder we should have both just as well as TS just as binary in that folder so so we can run that executable so npx TS hyphen just config colon in it okay and then you can see that a config file was created just. config.js and then you'll see over here we have this all set up where the preset is set to tsj and the test environment is set to node okay so now what we can do is we can go ahead and create some test file so I'll go ahead and create a new folder I'll call it underscore tests inside the source folder and I'll do this let's see I want to create I guess a simple test for get users so I'll create a folder called handlers and then new file users. test.ts and let me just make sure that I can actually run the test itself so uh we we still will be using uh just as the command to run the test it's just it's going to use tsj as the preset right over here so let's go back to our package.json file and let's uh modify our test script that was already provided for us if for some reason you deleted it or if it didn't provide this for you just make sure you add the test script and all you're going to do is just literally have it like this just just like that okay because you're going to run npm run test and it's just going to execute the just binary okay and it's going to see that we have this just config and it will know to use the tsj preset so now if I go ahead and do npm run test you'll see that it's going to try to run tests in uh the users. test.ts file but there are no tests currently so it's going to just fail I could also have just did npm test and that would have also worked as well okay let's go ahead and write a simple test for our get us users function so what I'll do is this we'll do describe uh get users and then we'll go ahead and use the it function and we'll just go ahead and say should return an array of users okay so again I'm not going to go over like the basics of how to actually write tests because I already did that in the express course I'm just going to show you how to actually set this up with typescript okay but what we want to do is we want to make sure we actually call this function get users and we want to make sure we provide uh mocks for request and response or I guess we can call it stubs so basically mock data so I'll just do this real quick uh let's do get users and then we're going to go ahead and do this I'm going to go ahead and create a simple mock I guess I'll do that inside a new folder called uncore Mock and we'll do index.ts because I'm going to be reusing this so we'll do export interface mock request or actually sorry not interface that's going to be mock request just like this and what I'll do is this I will pass in mock request and then I also want mock response as well let's do that whoops okay so let's see is it giving us an error okay yeah so right now you can see that it is giving us an error because it says argument of type empty object is not assignable to a parameter of type request prams dictionary any any pars cues okay so the easy thing that we could do is we could just actually C this as uh the request object and then same thing for mock response we could do the same thing whoops as response just like that and honestly because it's for testing it doesn't really matter all that much but let me just make sure that this comes from the Express Serve static core package so because it's for testing purposes it doesn't really matter so much that you're casting it so you can just do this you don't have to really like go out of your way and you know provide every single type annotation cuz that can be a little bit tedious so this is one way that you can do this you also could actually just cast it right over here so mock request as request just like that there's many different ways if you want if you really want to be explicit and properly type annotate every single thing you can but just note that it's going to be very very tedious because you're going to have to literally type annotate every single property in the request property and that's going to be a lot of properties and methods so so I recommend you just cast the object to the actual request type and that'll save you a lot of time okay so let's leave it like this and here's what I'm going to do uh so essentially for our mock for this test in order for this test to pass what I want to do is I want to actually make sure that the response for get users is sending back uh an array okay so what I want to do is for mock response I want going to mock the send function so send is going to be just. FN and I'm not going to have it return anything because we're trying to just mock the function itself we're not trying to override like what it returns okay now right now you'll see that I am getting this Arrow over here it's saying conversion of type this object over here to type response may be a mistake don't worry so much about this because honestly again we're using this as a stub it's not actually going to really affect the test or or the actual application in any way so what I'm going to do is I'm going to cast this to unknown which is what it recommends and then cast it to a response just like that and then you'll see the error goes away and then what I'll do now is this so we're calling get users and we know that get users is not asynchronous if your function that you're trying to test is asynchronous again make sure you use async and a weight so if get users was in fact asynchronous then I would have to await this before I write my assertion okay so I'm going to go ahead and write an assertion for mock response I want to make sure that uh let's see mock response do send was called so to have been called with empty array so basically I am asserting that this get users function whenever I call it it is returning an empty array okay so this test should pass let's go ahead and test this out make sure it works and there you go you can see it is returning an empty array of course if I were to remove this and then if I were to run the test again the test should fail and as you can see it it does call the function but it did not return the expected value which is an array so we know that our test is actually properly failing and properly passing it's not passing on a false positive okay so hopefully this shows you to set up a unit test so now finally I will show you how to set up super test for integration tests for your express application so this will require us to actually do some configuration changes in our index.ts file nothing crazy but all I'm going to do is create a new file and I'm going to call it create app. TS and again if you've seen that 8 Hour course and if you went all the way to integration tests you'll recognize this thing that I'm about to do so basically what I need to do is I need to create a function so I'm going to call it create app and instead of actually doing the initiation of my Express app in the index.ts file when I'm going to end registering all of my routes and middleware and Etc let me actually remove this route as well because I don't need that instead of doing that inside the index.ts file we're actually going to do that inside this create app function so what we're going to do is this we're going to import Express and then we're also going to want to import the users's router as well okay and because since our create app. TS file is in the same directory as index.ts all the Imports are going to be the same and then let me also remove these two because we're not going to need them anymore and then we're going to take this cons app and paste that inside the uh this create app function body just like that and all of the middleware all of the route registration will go inside here okay and then what we'll do is we'll simply just return the app instance right over here and then now in the index.ts file what we'll do is we'll import create app just like this and then we'll go ahead and do cons app equals create app just like that and basically when we call create app it's going to create the instance and it's going to register our routes register any middleware return that instance with everything registered and then now we can call app. listen in the index.ts file with the assign Port the reason why we're going to do this is because in our integration test file we're going to need to make sure that we can actually have an accessible Express instance okay so you'll it it'll make more sense once we actually uh set up the integration test so what I'm going to do is I'm going to go into my terminal and I'm going to go ahead and install super test because that is the library that we're going to use to set up integration test and run our integration tests so npmi super test and actually you know I want to install this as a Dev dependency just like that okay and now I will go ahead and just create a new folder and I'm going to go ahead and just call it uh e2e you can call it e2e or integration test whatever you want but I feel like there are a lot of overlap between integration test and E2 test so we'll just keep it in the e2e folder for now and I will set up a simple script for end to end tests specifically so right over here we'll do test e2e and then we'll go ahead and we're still going to be using just as the command to run uh these files run these e2e tests or integration tests and because we have our just config.js or J.C config.js file it's also going to use a tsj preset so we don't have to reconfigure anything so we're going to do just and we want to make sure that we tell this script where to look for test to run because all of our tests for n2n or integration test are going to belong in the E2 folder so we're going to use the test path pattern flag and we're going to pass in Source e just like that so it's going to look into the source e folder and run test in that folder all right so now let's go ahead and set up a simple test so we'll call it index dot I guess we'll call it test dot TS now what I'm going to do is import request so we're going to import super test and we're going to name the actual import name request so right now it's complaining it's saying that there it cannot find the Declaration file so we need to make sure we install the uh the types so let's go back into our terminal and let's do npmi hyphen D at types Super Test okay there we go and this error should go away here we go perfect all right so now we're also going to want to make sure we import create app that function that we just created and then inside our describe block so first we'll set up with describe block and let me see uh we we're going to go ahead and write a test to test get users so I'll just call this uh users I should I should probably rename index to users but we'll just leave like that for now uh let's do describe API users and so the way that we're going to set the set up is like this we're going to declare a variable using the let keyword called app and then what we're going to want to do is before all of our tests run okay so we're going to use the before all hook this is a just life cycle hook we're going to go ahead and call uh create app and assign that return value to app itself okay so you'll see that the uh return type is actually Express so you could actually type annotate this to be Express just like this you just need to make sure you import that uh from I'll show you from Express Serve static just like that whoops there you go uh let's see cannot use name spray Express as a type um let me see I think maybe we can import it from Express maybe this might work probably okay I'm not too sure about this but if you guys can think of a better way to do this definitely let me know down below I would really appreciate that but for now it seems like we have our uh type definition so that should be okay hopefully there's uh no issues uh let me see if I were to do it like this seems like it should be okay yep I don't think there should be any issues for now okay so now that we have uh called create app inside the before all hook and and assign it to the app variable we can go ahead and write a test so what I'll do is this we'll do it should return an empty array when getting API users okay and then what we'll do is we will declare variable called response and then remember request is a function and this function is is asynchronous so we're going to want to make sure we're using async await so async and then await the request call and then we want to pass in the app variable like this as an argument to request and then we're going to go ahead and call get and specify the path we want to actually make the request to or allow the super test to make the request to so slaps users and then now what I can do is I can write an assertion so I can do expect response body and then I can have it do something like to be or to strict equal empty array and let's go ahead and run this test and see if it works so let's do npm run test colon e2e that's the script that we set up to run n2n tests so you're going to see now it ran the test and it seems like it succeeded and if I were to actually let's say if I did like a different array with different values in the array the test fails okay so that's good because the actual return value is an empty array okay and you'll notice how if I actually um I don't know why I did create app over here let me remove that that should I shouldn't have done that there there we go yeah sorry about that I I did not mean to uh call create app globally over here I meant to do it inside this before all okay um okay good and some of you might be wondering well why should I do it inside the before all if it works like this right because if I were to run the test again right everything works why should I do it in set the before all so you don't really need to do it in this situation because right now we don't really have anything else that our express application depends on so in other words our express application doesn't have anything like a database that it needs to connect to doesn't need to connect to like a reddest client or reddest server so we don't have to worry about connecting it to those other services in a s in a situation where your express application has asynchronous uh calls that it needs to make to actually have the application uh up and running so you might need to connect your application to a database and that's very crucial for your application to work you're going to need to want to make sure that your application first connects to a database and then you want to actually initialize the app so in situations like that you want to make sure you do that inside the beforeall hook okay and also the before all hook allows you to add the acing keyword which allows you to use the a call in case if your create app function may run some asynchronous code so you cannot add async as a keyword over here it's actually going to complain see return R turning a promise from describe is not supported tests must be defined synchronously so that's the reason why but if you were to do that inside uh do that inside before all and if you were to run this even though create app is not going to return a promise it's not asynchronous okay but if it did this would work just fine so hopefully that answered that question so let me just remove this remove that so that's going to be pretty much it for this tutorial hopefully this all made sense I wanted to make a video to show you all how to actually set up typescript with an expressjs project show you how to do all the proper type annotations show you how if you're going to use a thirdparty library like Express session or passport show you how to install the proper type definitions and how you can actually reference those type definitions once you install them and those properties that you'll be using a lot to so hopefully this all made sense if you have any questions please feel free to leave a comment down below I read the comments pretty much every single day so if you have a specific question let me know down below join the Discord server if you need extensive help we have a community with over like I think a 2,000 members so someone will definitely help you out if you have a question so that's going to be pretty much it for this video If you all like this video please feel free to subscribe and like this video to help support the channel and I will see you all in my next episode peace out
Info
Channel: Anson the Developer
Views: 2,383
Rating: undefined out of 5
Keywords: express, express js, express js typescript
Id: Be7X6QJusJA
Channel Id: undefined
Length: 80min 12sec (4812 seconds)
Published: Mon Mar 11 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.