NestJS + TypeORM Tutorial | Repositories, Relations, Migrations & More

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys today we're checking out a deep dive of type orm and nest.js to show how we can integrate this powerful orm and interact with a SQL database using an sjs now with type orm we can easily Implement repositories set up relationships between different entities use transactions event subscriptions and so much more let's Jump Right In to see how we can easily set it up with an sjs I'll see you there real quick if you want even more on sjs and type orm I'm working on a new module for my nest.js microservices course which will use type orm directly and show you how we can work with it in a real world setting on this microservices project so if that sounds interesting to you check out the description for a link to a discount to the course I appreciate all the support and I'll see you in the video thanks so to get started let's use the nest CLI to initialize a new application we'll run Nest new I'll call this project an sjs type orm and I will use pnpm as our package manager and let our dependencies go ahead and get installed now we can run LS and see our newly created directory we'll CD into it and then we need to P npm install the dependencies for type orm so let's set those up now we'll pnpm install nest.js slash type orm type orm MySQL 2. you're going to use nest.js config which internally uses dot m to read in environment variables for our project so to set this up we need to pnpm install nest.js slash config finally we can run pnpm run start Dev to start our local development server which we'll watch for changes and compile and serve our application I've opened up the project in vs code this is a default and sjs project with a main.ts that bootstraps the app and listens for incoming HTTP traffic on Port 3000 by default we have an app controller with one get route exposed which is just returning a hello world string from our app service let's go ahead and test this out by opening up Postman and launching a get request that localhost 3000 if we sign this off we get that hello world response back with a 200 status code so we can actually go ahead and remove the app service and app controller files we're not going to be using these in our project so let's delete them and then in our app dot module we'll go ahead and remove the Imports as well as the controller and provider okay our first step is going to be actually running a local MySQL server that our application can connect to in our case we're going to use Docker to run our database server because the setup will be easy and it'll work the same on every system so to do this we'll create a new Docker compose file at the root of the project a dockercompose.yaml and here we provide a Services key where we describe our first service that we'll call MySQL you can call this whatever you'd like importantly we now need the image name we're going to use the default MySQL image from the docker Hub Repository we next need to specify some environment variables for our MySQL server now we can do this by specifying them here in plain text with a key in value or we can provide an environment file which will be loaded in our directory and automatically provided to The Container this is going to be the more secure approach because we can add that dot m file to our get ignore to make sure that the file doesn't end up in git which would expose all of our secret credentials so let's create this dot m file at the root of the project where we'll go ahead and specify the first environment variable needed which is the MySQL root password which is going to be the root password to connect to this database it should be any secure value I'll just call it random root password next we'll need the mySQL database which is going to be the name of the database that will automatically be created that we can specify here I'll call this Nest JS type orm so now now back in a Docker compose we can specify that M file which will be the dot m at our current working directory and lastly we'll specify a ports key where we actually expose the MySQL server Port that's running in the container to our host machine where we're actually running the docker image so we want to map the port 3306 on MySQL server to 3306 on our local machine so that our nest.js application can actually connect to it so next you need to make sure that Docker is actually running on your system I'm using Docker desktop on my Mac to run Docker if you don't have it I'll leave a link in the description to get Docker desktop it's very easy to get up and running on your local system so docker's running in the background open up a new tab in the terminal and go to the current working directory for our project and we will run Docker compose up which is going to go ahead and read our doc compose file and start the MySQL container which starts up the database server so you can see that our database has successfully started up and the server is ready to accept connections okay so now that our database is running I've opened up an application called MySQL workbench which is just a UI tool that will allow us to connect to our MySQL server if you don't have my SQL workbench I'll leave a link in the descriptions where you can download it for your system here we have the ability to add a new MySQL connection we can click the plus we can call the connection local Docker or whatever we'd like we just need to importantly specify this host of 127.0.0.1 which is just our local host address of our system and then connect to Port 3306 which is the port that MySQL server is bound to on our Docker container to our host machine so that's how we'll connect to it our username will be root and the password we will click on storing keychain and enter in the password from our dot m file for me it was random root password enter this in Click OK and now if we double click the connection you can see I've successfully connected to the nest.js type orm database which currently has no data in it but we can see this connection has been established okay so now it's time to set up a connection to the database in our nest.js app I want to create a new common database module where we can handle this connection establishment so we can quickly and easily use the nest CLI again and run Nest generate module database into the terminal which will create this new database module in a database folder as well as update our app.module to import it next we'll add an Imports array where we will actually connect to our MySQL server with type orm to do this we will call type orm module.4 root async which is going to allow us to specify an options object where we're going to use the use Factory property and now we can inject a service or any other dependencies in sjs we want to get access to the config service from nest.js config so I'll have a parameter called config service of type config service from nest.js config then we go ahead and return directly in object which will be the options for type orm now though we have access to the config service we can actually read in environment variables from a dot m file at the root of our project just like we did in the docker compose file before we forget make sure we add the inject property as well to the forward async object and now we actually have to specify the config service again to ensure that it is available in this use Factory method so also to make sure that we actually have the config service available to inject we need to make sure we set up the config module in our app dot module to do this we can add it to the first thing in our Imports array we'll import the config module and call for root on it we're also going to specify is global set to True which just means that we don't have to keep re-importing the config module everywhere we want to use it we only need to set it up once here in the root app dot module so in here we'll start providing the options the first one will be the type of database server we're using the type orm in this case it will be my SQL then we provide the host of the database so we can use the config service to get the my SQL host variable and we'll do the same thing for the port property we'll call configservice.get MySQL port and even better we can actually call get or throw which will throw an error if these environment variables aren't actually found which is exactly what we want to do we want to throw an error if there's some problem configuring the application so we'll call get or throw instead next we provide the name of the database we'll call config service again.getter throw this time we'll call this mySQL database which we know we already have defined in our dot m file from our Docker compose file let's keep on going and now provide the username which will be configservice.getter throw MySQL SQL username and we can copy this and paste it in and change this to the password which will follow the same pattern except username will now be password so let's go back to the dot m and provide all of this information we'll add the my SQL host we'll have the my SQL port which is set to 3306 we have the MySQL username set equal to root and finally we have the MySQL password set equal to random root password now like I said before since this dot m file is exposing sensitive credentials this is where we'd want to go to the dot get ignore file in our project and add the dot m to it so that we aren't committing these credentials I'm not going to do this so that the dot m file is available to you guys in the GitHub repository to make this project run as easily as possible so now that we've provided all of the information to type orm to connect to my SQL will provide two additional properties one will be Auto load entities and set this to True which is going to make sure that we don't have to manually tell type orm where our models are and have to do that manually we can just have it do it automatically which is going to save us a lot of effort now finally we have the synchronized property which if we look here actually will synchronize our database schema with the MySQL server on every application launch so this will be super useful when we're developing locally however it could cause to data loss in production so to make sure we check to see if we want to do this we can make this an environment variable as well and call configservice.getter throw MySQL sync renaise and we'll go to the dot m and add this as well my sequel sync Renaissance and set this equal to True by default and if we were running in production mode we could have a different dot m file which would set this to false so back in our terminal we can see the nest application is still starting up successfully and our type or modules have initialized successfully which means we have established a connection to the database and are ready to start writing and reading so now we're ready to actually start creating our first entity using type orm and actually be able to create read update and delete this entity we can very easily generate this boilerplate code we need to create a new entity using the nest CLI we'll call Nest generate resource and call this resource items we're going to be building sort of a virtual store where we can have different items for sale so let's go ahead and generate this resource you can see it's going to ask us which transport layer we're using in our case this will be the rest API and then we're going to select yes to generate crud entry points Nest has gone ahead and created a new items folder within this items folder we now have an items module with an items controller and item service the items controller is going to have all of the rest API routes to create read read one update and delete an entity using the item service and in the items service we just have some stub methods right now that are simply returning a text string we're going to go ahead and replace this with our repository now that we can actually connect to our database finally you can see our root app.module has the new items module added to it so our next step is to create a repository which is going to be how we actually interact with the database using type orm in order to create a repository for our items we need to fill out our item dot entity which The Nest CLI has generated in this entities folder let's start off by decorating this with the at entity decorator from type orm and then we can continue filling out the properties that exist on an item now these properties are going to map to columns in the mySQL database for the item table the first property will have on any resource in our SQL database will be the ID which will be of type number and we can specify that this is the primary generated column using the primary generated column decorator which will make sure that this primary ID will be automatically incremented each time we insert a new record next to just decorate a normal column we'll use the add column decorator from type orm and specify the name of our item which will be a string next up we'll have another column and this time I want to set a default value for it the default value for this property will be true so you can do this by specifying Default true in the options object to the column decorator this property is going to be called public which is going to be whether or not the item is public or private so now that we have all of the properties on the item one more thing I want to do is create a Constructor that actually will take in a new object which essentially will be a partial of the item we've just defined and then we'll call object dot assign on this which is the current class being created and assign all the properties to it from the incoming partial of the item this is going to make it really easy to create new entities in the future and not have to keep updating the properties in the Constructor we just need to pass an object that has some of these properties like the name and public and our Constructor will automatically assign them so now that we have this newly created entity we need to go to our items.module and inside side of the Imports array we need to create the Imports array and then we need to call type orm module and call for feature on it and here we provide an array of all the entities in the current domain so all of the ones associated with an item we need to provide the type RM module here by just importing The Entity and providing it to this array so as soon as we do this we can go actually go back to my SQL workbench and if we right click on our database connection and click refresh all we should now actually see we have the item table that's been created for us from type orm as soon as we told it about the entity it went ahead and synchronized our schemas as we've specified so that we have the correct table of course now with no data so let's go back to our items controller and start actually fixing up our apis to be able to support all this crud functionality to start off with our create route we can see that we're extracting a body and this was a type crate item dto which has been created for us in this dto folder and of course there's nothing here at the moment let's go ahead and fix this and specify the properties we expect when a new item is going to be created through our API the properties we'll first expect will be the name of course of the item and then we'll also take in whether or not this item is public or not so have the public Boolean now if we go back to our items controller we're actually providing the body from the request to the item service when we call Dot create so now that we have the create item DTI with its properties we want to actually persist it to our items table so our next step is to inject The Entity manager from type orm so let's open up a Constructor where we take in a private read only entity manager of type entity manager from type orm and what this entity manager does it is it exposes a number of methods from type orm that allow us to interact with the database so you can see we can execute raw queries with this entity manager but importantly what we can use it for is to actually save new records to the database and that's what we're going to use it for so let's go ahead and do just that we're going to go ahead and create a new const here called item and then we can just say new item we just want to instantiate this new item entity and we can pass in the create item dto directly because this matches all the properties on the item itself including the name and public property we'll go ahead and remove this return statement and now we will just call await this dot entitymanager dot save we'll go ahead and Save which we can see here we'll create new entities if they don't exist otherwise we'll update The Entity so all we have to do now is provide the item and it will take care of everything to save this and persist it to our database make sure we set this method to async as well finally go back to the items controller and we'll also Mark this method as asynchronous now we're ready to try to create a new item we can launch a post request at HTTP localhost 3000 slash items and we're going to provide a Json body where we specify the name and I'll call this first item and give it a public keyword of true because it will be public so if we send this off we get the 201 created response back and if we go to our database and now re-execute the query to look at all of the items in our items table we can see the item in the table here with the ID of one name first item and public set to one okay so next up let's tackle the find all route here so we'll change this to async as well this is just a get route that returns all of our items so if we check the find all method right now we need to use the item repository to extract items just from the item table let's go ahead and do that by injecting a new dependency called the item repository so we'll use add inject repository from nest.js type orm and pass in the item entity next we'll specify the private item repository of type repository from type orm and specify the item as the type here now we have access to the item repository we'll change the method to async and instead to this text string we're going to return this dot item repository dot find now you can see all the different find methods available to us in this repository we just want to call the generic finds to get all the records finally let's change this name here to items repository which makes a bit more sense because we can return multiple items finally let's make sure we add a read-only qualifier to the items repository just to stay consistent with our entity manager so let's test out our find all method now back in Postman we can launch a get request at localhost 3000 slash items and if we send this off we can see we get an array of all of the current items so we can even create another one by sending a different post request and saying second item as the name send this off to create the item so let's keep on going and back in our items dot controller we'll move on to the next route which will be to find one entity in this case find one item you can see it's a get route here that's taking a route parameter and we're extracting that using the at param decorator and calling it the ID so we can change this to an async method and we're calling find one in the item service and providing this route parameter as the ID of the item so to return just one entity with this ID we can call this dot itemsrepository dot find one buy which will execute a where Clause where we specify the ID of the item we want to look for let's change this to async as well and now if we go back to postman let's add this route parameter to the get request and say we want to look for an item with the ID of 2 and send this request off you can see we're now getting back just the single item with id2 if we change it to id1 we get the first item back next up on the update method you can see it's quite similar we have a route parameter of the ID of the item we are updating so we extract that here when the first parameter so we can change the method to async update and you can see the second parameter is the actual update item dto which we're extracting as the body so if we follow through where this update item dto is in our dto folder now currently we're just extending the create item dto in this update item dto I want to get rid of this and actually make this a dedicated class on its own because I don't want to be able to update all properties on the item I just want to be able to set whether or not it's public or not so we'll just accept a public property of type Boolean now we're going to set item Dot Public equal to the update itemdto.public which is going to update it with the newly updated property coming in on the dto finally to save we're going to call await this dot entitymanager dot save and pass in the item object that has the updated public property on it and typo or M will automatically save everything for us in the database let's go ahead and test this cell in Postman we'll send a patch request at items slash one and provide the public property and now I want to set this to false to Market as no longer public we'll send this off and get a 200 and now I want to get the same item back so I'll switch to get get this item back and we can see that public has now been set to false okay so lastly our remove method it takes a route parameter of the item we want to delete we extract the ID and call itemservice.remove with this ID to remove it we can simply call this dot items repository dot delete and pass in the ID of what we're deleting let's change it to an async method and then we will do the same inside of our controller and now we can launch a delete request at slash item slash one to delete this item we get a 200 okay with a delete response back however I don't actually want to return the delete response so we can go ahead and just call away this dot delete and now if we delete the second item as well you can see we have a 200 okay with no response and if we launch a get request at all items we should see we have now an empty array because no more items exist all right next I want to show you how we can Implement relations in type orm which essentially just abstracts the idea of joins between different tables in my SQL and it Maps it to different classes in our nest.js application so there's three different types of relations that we will review so the first relation is going to be the one to one which is a relation where a contains only one instance of B and B contains only one instance of a to make this more clear we're going to add a new property to the item that demonstrates this one-to-one relation and this is going to be called a listing a listing will just be a bigger description of the item with different properties on it so in our entities folder let's create this new listing Dot entity.ts and this will be marked with the entity Jack decorator as this will be a new entity with its own dedicated table and we'll export a class called listing this is going to have a primary generated column that's called ID of type number just like our item then we'll add another column to add another property called description of type string which will be the description of the item and we'll add another column to represent the rating of this particular item then we'll go ahead and add a Constructor that takes in the listing which is of type partial listing and call object.assign and provide this and the listing just like we've done in the item Constructor so now we need to go back to the item.entity and specify this listing is of type listing that we just created and then we're going to mark this with the one to one decorator which we can see here allows us to create a direct relation between two entities entity one has only one entity two entity one is an owner of the relationship and stores The Entity one ID on its own side so we will use the one to one decorator and we need to provide it the corresponding other type of the relationship which is the listing next we need to provide the join column decorator which if we look at the description we can see that it's used on the one to one relationship to specify that we are the owner of the relationship which in this case we are so let's mark this with join column so now that we've updated the item with a listing property let's go back to the crate item dto where we will update this to now accept a default listing we want to add a new listing property here and we need to create a new dto to represent this so I'll create the new dto file called create listing dot dtl which will export another class called create listing dto and inside of this listing dto we will just have the description that can be set of type string and we will set defaults for the rating on this listing so now back in the create item dto let's set the listing as the create listing dto now in our items.service you can see we have an error because we're not providing the listing so to fix this we need to create the listing object by setting a new const called listing equal to new listing from our entities import and then we're just going to go ahead and spread the create item dto dot listing to get all of the properties passed into the Constructor and we're going to manually set the rating equal to zero as our default rating now in the item Constructor we can spread all of the create item GTO to get all the properties and then we provide the listing object that we just created so now in our entity manager when we call Save not only will we save the item in the items table but we're also going to save any underlying entity that the item references through its relations in this call and that includes the listing here we're going to save the listing in the listing table because we described this relationship between the item and the listing however there is one property we need to set to actually enable this functionality which type O M calls Cascades to enable Cascades on this relationship we'll go back to the item entity and in the one to one decorator we're going to provide a new options object where we can set Cascade to True which we can see here means that the related object can be allowed to be inserted or updated in the database so we'll set this to true and now when we call Save we're going to actually update both of these in the database now don't forget that since we have a new entity here we need to actually provide it to our type over our module in the items module so pass in the listing to our four feature call now we can go back to postman and launch a new request a post request to create a new item we're going to give it a name of complex item we will set public equal to true and then we will set the listing property now equal to an object where we provide the description and say this is a great item so we'll send this off to create it and we get the 201 created response and now if we go back to my SQL workbench and refresh all of our data by clicking refresh all you can see we have this new listing table and if we select all items on it we can see that the listing was inserted into the listing table when we just made this request we have the description the ID and the rating and if we look at the items table now we can actually see we have the listing ID that actually maintains the relationship between this complex item we created and its corresponding listing in the listings table now you'll notice if we go back to postman and launch a get request to get all items we'll get the item back but we won't get back the listing on the individual item and that's because by default type orm is not going to populate any relationships on objects unless we manually tell it to do so so let's go ahead and update our find one route to actually do this we'll go back into the item service and let's change the find one method to actually populate all relationships to do this we're going to change the call to find one and provide the same where statement we did before we're going to look for The Entity by ID but now we're going to specify a relations property where we can set listing equal to true to tell type orm to populate this relationship when we query this item now if we go back to postman and launch a get request for this slash three item and fire it off not only do we get back its properties but we get all the details from its corresponding listing entry here in the database okay so that's the one-to-one relationship next we're going to check out the one to many or many to one relationship where a contains multiple instances of B but B will only ever contain one instance of a so to see what this looks like we will have a new property called comments so we'll call this comments and just like we did before we'll create a new entity and corresponding table called comment dot entity so we'll go ahead and Mark this as an entity and Export class comment now it's getting a little tedious to have to specify that ID property and the Constructor that's going to be the same for all of these entities so let's go into our database folder and actually create a new abstract dot entity T dot TS and let's go ahead and Export this class of abstract entity that has the primary generated column of type ID number which is the auto-generated ID property we want to be on all entities and we'll take in the Constructor which takes in the entity also we have type partial T so we want to make this abstract entity take in a type and then we can call object.assign this and pass in the entity so now we have an abstract way of describing an entity let's go ahead and actually use it in our new comment by calling extends abstract entity and then passing in the comment now we can provide two new columns the first will be just a basic column called content which will be the actual content of this comment then we're going to go ahead and specify the relationship to the owning entity so we do this by specifying the item that this comment will belong to and then we mark it with the many to one decorator which tells it to return a type of item and then we take in this item as another function call and specify where on it this property lives in this case item dot comments represents our comment class here now we can go back to the item dot entity and first of all we can get rid of the Constructor now and also extend the abstract entity get rid of the ID property and pass in the item type and then we can specify the comment entity now by passing in the type and this of course will be an array because this is a one-to-many and many to one relationship where we'll have many different comments on a single item we'll mark this side of the relationship with one to many because there will be one item and many comments and just like we did in the comment class we'll return the comment type and specify on the comment we want to return comment dot item on the item and you also need to make sure we explicitly import the comment from dot slash comment dot entity finally we'll go ahead and specify that options object where we can set Cascade to true so we can make sure that when we save an item we also save all of its comments in the same call lastly let's go back to the listing entity and extend the abstract entity in here passing in the listing where we can get rid of the Constructor and the ID property from it and remove the Imports as well lastly go back to the items module and provide the new comment entity to the type orm feature call so we're actually not going to take in any comments in the create item dto by default we want to set an empty array to specify there's actually not going to be any comments on a very new item so to specify this in our item Constructor let's set the comments equal to the empty array now if we go back to postman and send in a post request to create a new item we should see if we try to create a complex item to and send this off if we try to get just like before we can see we have all of our items with no relationship data let's go back and update the item service find one call to specify the new comments relationship and set this to true so that we return back any comments for a single item and we'll get the comments back on ID 5 of our newly created item so if we call get now we can see we get that empty array of comments because there are none so to actually be able to update and add new comments I want to update our update call on our items controller where right now in our update item dto we're only allowing it to be past the public property let's add a new comments property as well so in detail we'll create a new create comment Dot dto and Export this class of create comment dto and this is simply going to contain that single property called content of type string now back in update item dto we can specify the comments is equal to create comment dto array now we go back to the item service into the update call we want to Loop through all of the incoming comments and turn them into actual real comment objects that can be saved so to do this we'll create a new constant equal to comments and set it equal to update item dto dot comments and we'll call Dot map on it to Loop through every comment and we'll call this create comment dto we're going to return a new comment that just takes in the create comment dto into its Constructor and you can see we have an issue here because we need to explicitly import comment from the entities folder and now we can set item dot comments is equal to comments and now when we save the entity with the entity manager because of our Cascades the comments are going to get updated so before we do this let's go ahead and take a look at my SQL workbench to actually look at the state of our database we'll right click on the database again and refresh all data and now we see the comment table so if we select all right now it's completely empty so let's go ahead and change this in Postman we will update through the patch method to our fifth item and we'll set public to true but now we'll also add the comments array we'll provide a comment here with content comment one and we'll create another comment that has content set equal to comment two so now if we send off this request we have a 200 okay and if we try to get this newly updated item back with the get call you can see it has the comments that are saved on it that we just created now back in MySQL workbench if we refresh the query to get all the comments we can see we have our new newly created comments here comment one and comment two that both reference the item ID in this item id column so this corresponds to our item in our item table with the ID of five okay so the last relationship we're going to explore is the many too many relationship which means that a contains multiple instances of B and B contains multiple instances of a so in order to support this we're going to have a new table that actually handles all of the mapping or joins between these two many to many entities let's take a closer look at this with a example on our item we will now accept a new tags property that can be provided to a given item it can have multiple tags and these tags can be associated with multiple different items so let's create this new tag entity in the entities folder we'll have tag dot entity this will be an at entity that will export class tag which extends the abstract entity and we pass in the tag and just like our comment this is just going to take a single column of type content and it's going to be a string so now back in the item.entity we will now specify this array of tags from the tag entity and here we're going to specify the many too many decorator which allows us to describe this relationship to the tag so just like we've done before we'll return the tag type here and set Cascade to true so we enable Cascades now finally we need to also provide the join table decorator that's used in many to many relationships and this is going to specify this join table that we need to handle the mapping between many items and many tags we'll now accept a list of tags that can be provided by the user so let's create a new createtag.dto which is going to be export class create tag dtl o that takes in the content of type string now back in the create item we will take in this array of crate tag dtos so in the item service we can see we're now complaining in the create method again because we're not actually doing anything with these tags and providing them so to fix this we'll do something very similar to this Loop we did for the comments we will have a new cons called Tags and set it equal to create item dto.tags dot map to Loop over each crate tag dto and return a new tag that takes in the create tag dto and now in the call to the item Constructor we will pass in this tags array of new tags lastly in our find one call let's specify that we want to enrich this relationship by setting tags equal to True finally go to the items module as we've done before and provide this entity to our four feature call and now let's go ahead and create a new item we'll call Post items and now we'll specify the name of item with tags that will take in a tags array and this tags array will have a content type blue say this is a blue item and we'll have a another tag with a content of large to specify this is a large item finally we'll have the listing which will be an object with a description that says item with some tags so we'll go ahead and send this request off and we get a 201 created if we send a get request off to get all of the items we can see the newest item with tags has id6 so let's go get that individual item where we can see we have the tags that are now returned back with it and if we look in my SQL workbench to actually look at the state of the database we'll go ahead and refresh all of the data you can see we have this new item tags tag table and this is this join table that I mentioned that actually handles the relationship between an item and a tag because we have a many-to-many relationship you can see each one here is going to specify the owning Item ID and the tag ID associated with it so on the item itself there will be no mention of tags and similarly on the tag itself there's going to be no mention of the item because the join table is going to handle all of that relationships all right next up let's look at how we can Implement transactions using type orm now a transaction in a relational database or really any database it's just the idea of maintaining consistency when we have multiple updates to that database in a single request so to demonstrate this let's take our update method and temporarily change it so that we can show how a transaction would work in type orm to do this I'm going to go ahead and comment our existing update code and have a new block where we will do a transaction now to start we'll call away this Dot entitymanager.transaction and here we get the async method with an entity manager provided and we simply can execute all database operations in this callback using this entity manager and it will be run inside of a transaction now what this means is if at any time in this callback an error is thrown and we don't complete delete the whole transaction then all database updates in this callback will be rolled back and nothing will persist so let's go ahead and take a closer look at this so we'll copy our commented code right now and paste it in the Callback and instead of calling this dot entitymanager.save we're going to use the one passed into us where the transaction is taking place now let's say after we save the item on our database we want to take that item send it to some external service and get a new tag that's generated just for that item using some of the data on an item so to simulate this we'll go ahead and create a new tag that's going to be equal to just some random value so we'll open a template literal and call math.random to generate this new tag so we'll set this equal to tag content and then we can create the new tag by calling const tag equal to new tag and pass in the content of tag content now we'll call await entitymanager.save and save this tag so you can see we have two different rights to the database happening one for the item here and the subsequent save of the additional tag let's go ahead and try updating our most recent item so we will update using the patch method on item six and we want to change the public property to false now and we'll go ahead and add a empty comments array and send this off so we have a 200 okay and of course we can get back the item with the public set to false and our comments sent to an empty array so this update did succeed now if we go to mySQL workbench and run a query to get all the tags we can see we had this new randomly generated tag that got added to the tags table because of our second right on our update call now to simulate a failure in this transaction I want to throw a error by calling throw new error right after we save the item so after we save the item we're going to throw an error and we should see that the item gets rolled back and our update is not persisted to the database because of this error in our transaction so let's test this functionality out we'll go ahead and get the current state of item 8 and in our system by calling get slash eight to see that it currently has no comments so now I want to send a patch request to update this item and actually give it some comments so I've gone ahead and updated my payload here to represent the comments array and now if we send off this request we of course get a 500 internal server error because we are throwing an error however now if we actually get the item again we can see that it still has no comments being generated even though our error is being thrown after we save the item this transaction rolls back this right and we're left with the original state of the item with no comments all right next up let's look at event subscriptions using type orm so event subscriptions are going to allow us to listen to a number of different events to our data and our tables and actually respond to them programmatically so we can see how this is can be done really easily let's go into our items folder and create a new file called item Dot subscriber dot TS and we're going to decorate this class with an event subscriber decorator an export a class called item subscriber which is going to implement the entity subscriber interface and we'll pass in the item entity that we're subscribing to next we'll have a private read-only logger set equal to new logger from nest.js common and pass in the itemsubscriber.name and then in the Constructor we're going to inject the data source from type orm and make sure we call data source dot subscribers dot push and actually push this class to the subscribers array then we'll go ahead and specify a listen to method that simply returns The Entity we want to listen to In this case the item so now at this point there are a bunch of different events we can subscribe to in type orm I'm going to leave a link in the description where you can see a list of all of these different events that we can subscribe to I'm going to show you two different events the first is called before insert and we can even see all of the different all kind of events here that I start with before we're going to use before insert where we can see we get the item which is an insert event of type item let's go ahead and call this.logger.log and we'll say before insert and the second property we'll call json.stringify and just stringify this event to the console next we're going to have the after event where we can call after insert and we'll do the same thing so we'll copy and paste this log statement and just call this after insert and importantly instead of stringifying the entire event we want to make sure we stringify the event dot entity which is where the actual entity or in this case the item actually lives on that object we want to provide this to the provider's array that is the item subscriber to make sure nest.js knows about it so now back in Postman we'll send a new request to create a new item with a name of item with tags plus events and send this off to post slash items you can see we have a 201 created and now if we look in our console logs we can see the before insert log with the item that we are inserting and then we can see the after insert call with the object logged to the console so there's a bunch of different events that we can subscribe to and this can be super useful when trying to do things like listening for data changes and mutations in our system and type orm makes this very easy to do with event subscriptions next up I want to show you how we can execute migrations using sjs and type orm migrations are going to allow us to seed and change data every time we start up our application or execute a command so in order to do this we need to specify a new file to root of our project called type orm.config.ts and this is going to be the data source that we expose to the type ormcli to actually connect to our database so in here we're going to export default new data source from type orm and here we have the options object where we provide the options just like we did in our Nest application this time we'll specify the same type of MySQL and now we need to provide all of the environment variables to connect to this database through the config service just like we did in Nest so to make sure our environment variables actually get loaded we need to open up our terminal and make sure we pnpm install dot m which is the underlying package in sjs uses in its config module we're going to call the same method to initialize our environment variables which will be config so call config and we import this from dot m so this is going to load our DOT M from the root we can see and now that we've loaded in we want to get the config service and set this equal to new config service from nest.js config so now we can actually use the config service let's go to our database.module and copy and paste all of these environment variables into the type orm config and in doing this I see we have a typo in our config service here let's go ahead and replace this all at once in vs code I'll search for this typo and replace it with config service to update this in our type orm config as well as the database modules now it's config service correctly so we're providing all of the M variables from our dot m to connect to the database on the CLI to execute these migrations the one other thing we need to take care of is to provide the list of entities to this data source now in our Nest app we had the ability to do this automatically through this Auto load entities property however the same cannot be done through the data source directly so we need to load our entities manually by calling all of our entities in our system so we'll add the item listing comment and tag as all of the entities in our system finally let's go ahead and specify a migrations property where we're going to specify that all the migrations in our app are going to live in a migrations folder and set a wild card so that all files in this folder will be run as a migration so in a root we'll create this new migrations folder where all of these files will live and now we can actually set up our package.json scripts to generate and execute these migrations the first step will be type orm create migration so this script is going to use npm directly and call npm run type orm so we need to be able to reference type orm directly in our project so to do this we'll add this type orm script which is simply going to execute TS node and go into our node modules folder into type RM and execute the CLI command so now that we can actually execute the CLI we are going to call migration create and specify that we want this migration to be generated to our migrations folder by using dot slash migrations slash npm config name which is going to take the name of our migration we want to create so let's go ahead and test this out on the terminal we're going to head and run npm run type orm create migration and then specify the dash dash name which we want to be called public items because in this script we're going to go through our items table and set all of them to public so let's go ahead and run this to generate this migration and now we can see in the migrations folder we have this new file that has the JavaScript timestamp as well as the name public item in here we have two methods that will be implementing this migration interface so each one of these methods takes in the query Runner which we can use to execute raw queries against our SQL database the up method will be called when we want to execute all migrations and the down method can be called to revert our migration let's see how we can do this by calling on the query Runner queryrunner dot query and here we're going to call update item set public equal to one because we want to update all items to make them public now in our case we're not going to have a down method so let's just simply have a new private read-only logger in this class from nest.js common we'll pass in the constructor.name and make sure we call this.logger.log I'll specify up for the up method and in the down method we'll log that we are going down and we don't need to use the query Runner so back in our package.json let's add the two scripts we need to be able to execute the up and down methods we'll have type orm run migrations and this is going to be equal to npm run type orm migration run will pass Dash Dash and then pass Dash D to specify the path to the type orm.config.ts which specifies how to connect to the database we'll go ahead and copy this command paste in revert migrations and simply call migration revert instead to execute the down function so before we try this out let's go to our database now and look at all of our items to see the state we can see all of them are currently public so I'll go ahead and set a few of them to zero to represent that they are not public now let's go ahead and execute these migrations by going back to vs code and in the terminal we can run npm run type orm run migrations you can see a bunch of different information here including the actual queries that the migration Runner ran including our query here where we called update item to set public and here is our up log statement inside of our migration script and you can see that it has been executed successfully so if we go back to the database now and refresh this query we can see they've all been reset back to being public because of this script lastly if we want to show that the down method can be run we'll just call the same function but now we'll call revert instead of run we can see the down log statement let's take a look at how we can do unit testing with this type orm integration so in our items dot service dots back right now we can see we have a test for our item service that just check to see if the service can run so let's go ahead and try running this by running pnpm test items dot service now you can see the test has failed because we aren't providing a provider for the item repository so it really has no idea where to get this from typo where I makes this really easy for us by providing a special function to be used in these unit tests and we can make use of it by going to the provider's array and providing a custom provider the provide property is going to be key here this is where we call get repository token from nest.js type RM and then pass in the entity we want to get the repository for of course we know from the item service this is the item here which we pass in to inject repository so let's pass in that same item then for use value I'll do is passed in an empty object to represent our mock repository you can set this to any property you want and mock these methods and listen for them to be called in your tests so now if we run the same pnpm test to run the item service you can see it's still failing but now it can find the repository it just has an issue finding the entity manager and this is also expected because we're not providing it so let's do the same thing here now in this case we can reference The Entity manager directly from type RM and we'll just give it a use value of an empty object as well to represent an empty mock just like for the repository you can fill this in with mock methods let's go ahead and run the test to see it get executed we can see the test is now passing so let's go ahead and show you how we can actually write a test for one of these methods let's go ahead and test the find all method so this is a very simple method we want to just test in the item service that when we call find all we're calling find on the items Repository so let's go ahead and firstly get access to the items repository so that we can reference it in the test here we'll have a new let items repository set equal to repository of type item then we'll get it by setting item repository equal to module.get passing in repository item to give it a type and then we can pass it and get repository token to get the repository token for an item so now that we are actually getting the items repository we need to mock the method on it so let's do this in our use value we're going to set find equal to jess.function which will mark this as a mock function now in find all we can say that we want to call our service dot find all which is asynchronous so let's set this to async when we'll call await and now we can expect by calling expect items repository dot find to have been called so we're looking at this mocked object and the mocked function on it we're saying this should have been called when we call find all in our service we which is what is happening in the method so let's see if this test will now work we'll open up a terminal and execute pmpmtest items.service we can see the test has passed and we are successfully calling the items repository so we can see how easy it is to mock both the repository and the entity manager in our tests so I hope you've enjoyed this deep dive into type orm and SGS this integration is really great and powerful it allows us to build restful apis with SQL very easily thank you so much for watching and I will see you in the next one
Info
Channel: Michael Guay
Views: 37,138
Rating: undefined out of 5
Keywords:
Id: 9MGKKJTwicM
Channel Id: undefined
Length: 66min 30sec (3990 seconds)
Published: Fri Jun 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.