How To Handle Errors in Node.js and TypeScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome to another video tutorial today we're going to talk all about error handling in nodejs and typescript using the popular framework expressjs so let's get [Music] started now if you're not familiar with the concept of error handling in JavaScript in general or you don't know what an error is or what an error handling means I recommend that you check out my other tutorial on error handling in JavaScript first and then you can come back to this tutorial and learn error handling uh in nodejs and typescript so you can see in front of me I have a boiler plate for uh nodejs application running with typescript I have already configured typescript with uh nodejs and already installed the required dependencies and Dev dependencies so let's go through the entire code base and see what kind of Errors we have before we we do that I want to tell you that all all the code that I will write in this video will be available on my GitHub repository uh so make sure to check out the description box you can see the link to my GitHub repository so you can Fork it you can uh play with it and keep it as a reference for you in the future when you want to apply error handling logic in your applications um you can see that my application is running on Port 3000 and also I have uh some some functions or request handers or controllers or whatever you want to call them so I have these um request handlers okay HTTP sign up sign in and sign out and inside these I have already thrown some errors for example here sorry are not available and Here sign in error Here sign up error and also in the index TS you can see that app. all AB do all means that um all the HTTP methods for example example uh post delete put patch get whatever it is all these methods will be uh handled by this request Handler on this route this route is star star means anything else anything not sign sign up or sign in or sign out anything else will be handled uh by this uh request Handler so um I forgot to here to throw a new error okay so anyway when we throw errors inside our application inside our uh request handlers all these errors are handled inside something called a middleware so what is a middleware uh in order to understand middle Wares I want to show you some diagram okay you can see in front of me I have uh here uh web browser or web client in dark mode because I know most of you don't like light mode so in light mode I have sorry in dark mode I have a web browser that goes to Local Host 3000 and probably here 3,000 sign up okay so I go to http Local Host 3000 sign up and this will go uh in itself to this request Handler which is HTTP sign up if you remember HTTP sign up is the same as this request Handler so when we send this request uh the request goes to this request Handler and the request hander will apply some logic and after it applies its own logic it will uh respond with uh this response object and this response object reaches its destination which is the client or the web browser and middleware middlewares are just interceptors they are called middlewares because they stand in the middle between uh our client and between our request handlers so whenever we send this uh request object the request first will be handled by this middleware and this middleware can apply some logic to this request and after that it will pass it to the second middleware and so on and so on until finally it reaches the end of the chain which is the request Handler us HTTP sign up in our case and after this uh request hander sign up applies its own logic to the request it sends a response so this response will go to the middleware two and then middleware one and these in turn can apply their own Logic on this response object until finally this response reaches by the end to the web client which is the web browser in this case so that's basically what a middleware is now if we go back to the code base okay uh in our code base we defined an error Handler middleware okay and error Handler middleware is just a function okay any middleware not just error handling middlewares any middleware is just a function that has uh few parameters but specifically when we talk about error handling middle Wares we must Define four parameters and these parameters are error which is the error that is being thrown and request which is the request that we need to intercept if we want to intercept and then the response and then next next is just um function that calls the second middleware in the chain if if you remember if we have different middle Wares one after another we call next in order to uh go to the second middleware so uh that's next and if we want to apply this error handling middleware inside our application we need to pass it to this app.use method uh app.use is used to pass middleware uh to it so basically we Define app.use error Handler or error Handler middleware and we put it at the end of the application not in the beginning because um if we put this for example here uh if any if any error happened inside any of these functions the error Handler will not work this error Handler must be defined uh at the end of the application so we need to write it uh as close as possible to the end of uh of the file so basically if we want to handle errors in no GS we simply apply some error handling logic inside this middleware okay for example let's for now let's see console log error dot dot name and console log error do message okay so if if I go to here okay and go to Local Host 3000 and go to sign up you can see the error is logged first we see the name of the error which is error and then we see sign up error and if I go to sign in the same thing and same thing for sign out you see and finally if I go to something that it doesn't exist it will be handled by this request Handler okay Catch Me If You Can so let's stop this that's it for error handling in nodejs for synchronous code if you if you see that in all our controllers or uh in all our request handers they are all synchronous code but in real life scenarios we don't have synchronous code that much when we when it comes to error when it comes to request handlers because we uh usually talk to database or we call another API or something like that so uh if I add async for example ah sorry not here async function okay and now let's go to Local Host sign up so basically we are mimicking the scenario of creating a user inside the database uh and it's asynchronous method function and we need to await it in some way so let's go to sign up ah you see the application crashed it says application crashed and there is a very bad experience for our uh user so we need to find a way to uh fix this error the problem is that our error is not being handled and instead it crashes our application so the reason is whenever we throw errors inside acing functions uh we we can't throw it like this we need to use another way which is I want to define the uh parameters here which are R next okay we have some errors errors from typescript because I want to say that this comes from request from Express and here as a response from Express and also here it's next function okay so now instead of throwing the error I need to call next that's how you handle asynchronous errors inside uh expressjs you define the next function so let's try now sign up yeah it already logged you can see error sign up so we handled the error correctly now let's see some of the problems that we have inside our code base the first problem that we have is that when we throw errors there is no way that we can differentiate between different types of Errors we have uh very general word here it's just error okay we can't know the the cause of the error maybe the error is caused because of the uh database crashed or maybe because authentication error or maybe because um a page page not found or something like that so uh that's that's a good problem and also if we go to the error hander middleware and here instead of logging the error I want to return something because remember that we are dealing with fronted engineers and all the errors that we are handling will be sent to the um to the front end engineer so we need to return something I want to return res. status and then Json uh return an object as Json which is message error do message so you can see now that yeah I I receive an error uh as a as a as this uh adjacent object message sign up error but the problem is we are hardcoding the value in the r do status um for example let's suppose that the error happened because unauthorized uh unauthorized operation and in that case we need to write 401 and if we have for example um database error we need to return 500 if we have page not found we need to return 404 so there are different status codes that we need to need to return so this value must be C must be able to be customized right and also the message that we are sending here okay uh the message that we are sending in this case it's very simple error. message message is just a string um so it's very simple in nature but sometimes the error that we are receiving is complex in nature and it has um maybe it has a property and that property has an array of objects and inside these objects we have other objects so we need to format or uh customize this uh error object that we have in order to extract useful information from it and return it to the front end maybe we don't need to return the entire error object so we need to find ways to solve this complex problems because we are writing code that is scalable okay so let's see how we can solve this problem um the first thing I I'd like to see is inside my controllers instead of writing throw new error I want to write instead of just the general World error I want to return or throw for example here authenticate error and here maybe instead of General word I need to return database error and maybe here it's bad request error so all these classes that we have defined here are not are not true classes we need to uh first create them so in order to create them let's create a folder inside the source directory I want to call it errors and inside it I want to create a file called database error. TS and inside here I want to export a class called database error and it extends the built-in error Constructor and I want to call the Constructor function and inside it call Super and I want to add a magical line now I know that this magical line seems very weird but if you are curious enough to understand what is going on behind the scenes and what this magical line does uh I will link to a resource in the description box to a stack Overflow uh post so you can read and understand for yourself but for ourself we need to write Set uh object dot set prototype of this to be database error. prototype again if you want to learn what this line does behind the scenes you can go to that stag overflow discussion and see for yourself so anyway I Define this um error here anyway I Define this custom error class which is database error and I want to I want to come back and import it and you can see that we are not sending any message so I simply can remove this because in database error when we instantiate the the uh class like in this case new database whenever we instantiate any class in JavaScript or typescript the Constructor method will be called so if you don't pass anything any parameter to the The Constructor then you cannot uh pass any string here for example okay typescript will say it's an error so anyway if we go and def find this one as well authentication error sorry not here in the errors dots and I'm going to copy paste everything just to save some time authentication error okay I can import it now also I don't need to pass anything and then the bad request the bad request is also not defined yet so we need to create this file which is bad request. TS and here I want to copy paste everything and no not here if I go back to here I want to copy this one yeah I know I'm too lazy so yeah what is left is to import it now in this case we are also receiving errors because we are passing a string to the Constructor but I don't want to remove the string I want to really pass it so I need to go back to the bad request error Constructor and here I type public message it has to be a string okay and this message uh I I want also to pass this message to the error Constructor for logging purposes okay just for loging purposes when we log the errors inside the terminal we need to see this message and let me do the same thing for other uh errors here but I don't want to pass a message I simply want to define a a hardcoded string which is an user unauthenticated for example okay and what else we have the bad request no we have database okay database error I also want to pass a string to the error which is database crashed try again later so remember the super class uh sorry the super method calls the error class and pass it this string so now let's see if I go back to my error Handler middleware if you remember we did all of this refa factoring just in order to differentiate between the different types of error that we have inside our application and so far we have three different types of Errors so let's see if we can differentiate between them if we write if error instance of database error so if this error was an instance of the database error class I want to return okay I want to return not for 404 I want to return 500 and here I want to return different message which is uh database error and let's see also for authentication error and the same thing for finally bad request error here I want to say authentication and here it's bad bad request okay so the problem solved the problem is we can now differentiate between uh between different uh types of errors that we have inside our application but now we have a different problem which is our code is is not dry we are repeating ourselves imagine that we have uh 20 types of Errors so for every type of error we need to define a different uh check and change the code and change the message so it doesn't make sense to to act like that uh another problem we have not just uh not just that we are doing repetitive code code second problem is uh inconsistency now any anytime a developer can come and write for example he defines some fun some uh file here it's called not found error and wrote ER not found error here not found error I know it it doesn't exist yet but let's assume that it exists and instead of passing 404 the developer passes 4,000 4 so it happens and also maybe instead of message the developer writes MSG and write something not found or maybe instead of passing um a string maybe passes something else like an array or something okay I can pass an array and um there is no error typ script is not complaining about that so we need to find a way to make our code consistent and also to to make our code expected because remember we are dealing with frontend engineers and we don't need to cause any headaches to front end Engineers by sending different properties and uh different content inside here I if I want to return a property called message and in this uh in this error it's a string I want to always return a string and if I decided to change my mind and return uh an array for example I want typescript to tell me yeah you are doing something wrong and also for the status codes I don't want to hardcode everything I need to to find some way in order to write it uh somewhere I will show you now where and this will be populated um by default anytime we we see that this is a bad request error or this is authentication error so to do that let's remove this code to do that I want to go to database error and here I can simply write status code equals in this case what is it 500 and in bad request it's 400 and in authentication error it's um 401 okay now I can simply go to back to my middleware and instead of hardcoding these values I can write error do status code okay so we solved half of the problem but still we are repeating ourself so let's see how to handle this thing uh in order to show you the the solution I want to show you some diagram in order to think make things more clear so far if you remember all these custom errors database error bad request authentication or whatever they are all okay all these errors are extending the built-in error Constructor and after that we go to the middleware and in the middleware we handle these errors uh by checking if the error that we receive is of this type or this type or this type so that's why we are repeating ourselves the solution that I'm going to implement is going to look like this all the custom errors that we are defining in our application instead of making them extend the built-in error class I want to create a custom error class which is called also as abstract class and this abstract class will extend this error and this custom error will be extended by all of these errors here authentication bad request or whatever and after that in the middleware instead of checking any error is coming from database or authentication or bad request I want to check if the error is an instance of this custom class so this custom class is called in the world of software development it's called Uh an abstract class uh an abstract class is simply a class that um that cannot be instantiated first so we cannot call throw new custom error uh an abstract class cannot be instantiated and also an abstract class acts as a blueprint for all these classes okay it acts in a blueprint and it tells all these classes that extended that they must com comply to some format or to some blueprint okay I will show you now instead of just talking I want to show you inside coding if I go back here to okay I want to create um a u a folder called utils and inside it I want to create custom custom error dots and here I want to Define uh export abstract class called custom error and in it itself it extends the built-in error Constructor and then call the Constructor method and then call the super the super method will be passing uh some message to the error so I want to Define public message as a string and here I want to pass as the message okay what else so um I want also to create status code and this status code or let's say it's status code capital I know it's not the normal way of defining things but I'm writing it for a reason and I want to mark it as an abstract property and also I want to Define an abstract method which is called serialize which is a method that returns an object of message and that message is a string okay now let's make all our errors extend this custom error so here instead of uh extending the error built-in class I want to to extend the custom error the abstract class so I want to import it you can see immediately that I have some errors the error says that non-abstract class does not Implement all the abstract members of custom error remember in in custom error we defined status code but here it is status code with lowercase s so I want to change that and also I want to Define some method which was called serialize and it has to return a message of unauthenticated user for example it has to be a string whatever string it is okay and now you can see that typescript is no longer complaining about this and if I decided to change my mind and write for example serialize error typescript will go crazy and will tell us that there is an error and the error is because we are writing the wrong name so that's how you uh extend uh an abstract class let's apply the same thing for bad request custom error and here change it to SC capitalized and Define some serialize method and this steralized method returns an object of message uh this do message okay remember this Dot message is this one and it is a string so we are good to go and whenever we Define something else we get an error so let's go back to database error finally and do the same thing here is It's s capitalized and finally we have what method it is serialize I forgot and return a message of string and I want to return this same one okay and in the middleware we can see a bunch of Errors um because it detects that they should be another name so now what kind of problem we solved by defining this uh this uh what it is custom error abstract class what kind of problem we solved by creating this the problem that we solved is now we no longer need to um worry about what kind of Json I want to return I no longer need to worry about what kind of uh status code I want to Define and for any reason if I decided to write here um status code D for example typ will say that there is an error so I need I I no longer need to worry about what kind of status code I want to to pass and my code if you can see my code is consistent status code status code status code and here also instead of returning this I want to return error do serialize and I can copy paste this here and here and you can see how consistent our code is error the status code it's written all the time and if any developer decide to change his mind and Define let's suppose creating um okay I want to copy paste this quickly if some developer working on our code base decided to Define not found error for example not found error and if he change his mind and instead of writing status code with capitalized s and WR s lowercase typescript will throw an error if he decides that instead of serialize I want to say it's format or reformat or whatever typescript will say an error serialized basically means the same thing as format um but we we decided to call it serialized not format U we call it serialized because as I told you before our our error that we are receiving uh might not be a simple uh a simple string um or a simple object object it might be an array of objects and inside this array we have different objects and different objects have different properties and these properties have arrays it's possible that we receive errors like that so we don't need to um to assume that the error that we have is simple and simply return it we need to define a method like serialize and this serialize uh Returns the the implementation of it is different from fun to from class to class Maybe the database error receives a very basic error like this maybe the not found is simple but when it comes to authentication the error that we receive when we throw an authentication error maybe that error is complicated and we need to serialize it or reformat it in some way and send it back to the client so if any developer comes and defines a new uh new error and a new custom error and write something that is not consistent with our codebase typescript will will complain and say this is an error so we solved the problem of inconsistency and now the second problem that we have is not dry code or repetitive code so basically we can remove all of this now let's test our application if I go to postman let's start with sign up you see it's 500 and the error message is being logged and sign in the same thing it's 401 unauthorized sign out in sign out I get this message with bad request 400 so our code is running so the last thing that we are left with if we are if we go to something that doesn't exist if you remember yeah the postman is hanging I will show you later why if I go to index DS in index DS I'm throwing an error this is general error so if you are throwing a general error I want to uh you can see that the general error that we have where is it in index TS okay this error uh that is being thrown is not an instance of custom error so that's why uh that's why Postman is hanging because it doesn't know what to do with it so let's see if I return rest do status 400 and then Json something bad happened let's save and see now yeah something bad happened but again um I returned the string you can see the problem I returned the string I forgot I have to return message of this okay so you can see that I already wrote an inconsistent code while teaching you how to write consistent code I know it doesn't make sense but you can see that it's something that happens uh let's see now you can see that message um something bad happened add with four 100 bad request and that is a proof that our application is working fine and our error handling is robust so that's how you create error handling logic that is scalable that is reliable and robust and you write dry and consistent code thanks to the power of abstract classes if you like this video press the like button and subscribe to my channel and write me some comments and tell me what kind of content you'd like to see in the future and also uh give me some feedback on the current content now and until then see you very [Music] [Music] soon
Info
Channel: Mark Maksi
Views: 5,423
Rating: undefined out of 5
Keywords: error handling, nodejs error handling, nodejs error handling with typescript, error handling in nodejs, nodejs error handling typescript
Id: 7WGVCx7Detc
Channel Id: undefined
Length: 37min 22sec (2242 seconds)
Published: Fri Dec 29 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.