Okay, so we're ready to get started building out our common module.
And the first part of our common module is going to be our persistence layer.
So working with a database and working with an ORM so that we can interact with the database and save
entities in our microservices.
So we're going to use MongoDB for our database layer.
And we're going to build it in a very abstract way so that we could potentially swap the database out
with a SQL database using something like type ORM.
So we're going to build this in a very generic way, a very abstract way.
Follow best practices.
So let's jump right in on the command line.
We're going to need to add a few dependencies.
So I'm going to run npm install nestjs mongoose and then mongoose itself here.
So go ahead and install these dependencies.
So in addition to adding our database module here we're also going to add our configuration module which
is going to allow us to read in environment variables using a dot m file.
Using the dot env package is what Nestjs uses under the hood.
But this is going to make it so that we read in environment variables, and we can change these variables
depending on where we're running.
If we're running locally or in production, we can change that out for different values like our MongoDB
Uri and other configuration variables we will need later on as we build out our microservices.
So to add that, we'll run npm install nestjs slash config.
Okay, so now we have all the dependencies we actually need.
We can use the nest CLI again to actually generate our common modules.
So we'll use nest generate module and then do dash P where we specify the project we want to pass in.
Of course if we go to our nest cli json we can see the list of projects we can add things to.
In our case of course we're going to add to our common library right now.
So let's add the common project here.
And of course make sure we also add the name of the module.
In this case we will call the first module database.
So now we can see that the database module has been created and our common module has been updated.
So if we go back to our code editor we can see that empty database module.
And then our common module is updated.
So I don't actually want a common module or any of these other common files out of the box.
Because each of our independent microservices will import the common modules they need and only the
ones they need, and also configure them independently.
So we don't need any common module here.
Instead, our microservices will import the database module individually with options.
So now that we've created our database module let's do the same thing for our config module.
So this config module is going to be a wrapper around the Nestjs config module.
And so now inside of the config module we can actually set up the imports array here.
So our imports array is going to call the nestjs config module and call for root on it.
And so of course we are going to have to import the config module from Nestjs config.
And we're going to put it in as nest config module here so that it doesn't conflict with our config
module.
And the reason why we're abstracting our own config module and wrapping it around the nest one is because
if we were to change the underlying configuration module, in this case the nest one, we only need
to do it in one place.
So it's always a great idea to try to abstract these third party dependencies when we can.
And that's exactly what we're doing with these modules in the common library.
So now by calling nest config module for root, we are telling the nest config module to load in any
environment variables that we have in memory, and also to read in any dot m files we have in our directory,
which will be super important later on when we use the config service to read in these environment variables.
However, for now we can leave things alone and go back to our database module where we can prepare
to initialize.
So before we initialize the database module, make sure we go back to our terminal here and make sure
we start up our development server in dev mode by running npm start dev.
So we start listening to changes and make sure that our app starts up okay.
We can already see a couple of errors coming from the terminal here on our index.ts, which makes sense
because we deleted our common files.
So we just need to delete these exports here.
Now if we go back, we can see that our nest app has started successfully and we can continue building
out our database module.
So if you've seen my videos in the past, you know how easy it is to set up a connection to mongoose
here, all we have to do is add an import to the.
Mongoose module from Nestjs mongoose that we installed earlier and called for route on it, and then
we can pass in the connection Uri for the database.
So if we were doing this for a very small application, a simple one, we could just simply pass in
the connection uri localhost and pass in the name of the database.
In this case sleeper.
And this would work perfectly fine.
And to even test this out we can go into our source folder here and in our imports array.
For our route app module we can add the database module that we just added.
And notice here how when we added the database module, it automatically imported it into the libs folder.
So we want to keep this import nice and clean in case we change the directory structure.
We want to target app slash common because if you remember from earlier we updated our Tsconfig here
with a path section that says if we use app slash common look in the libs folder.
So if we ever change the directory structure, we only need to do it once in our tsconfig here.
And of course now if we come back, we can say that Index.ts is not exporting this module.
So this is going to be important that whenever we want to use something from our common library, we
need to export it from the Index.ts here.
So we're going to export everything from the database directory here.
And then I'm going to add an index.ts to my database directory and export everything from the database
module.
So now if we go back to our root app module in our app, we can see that there's no more errors here.
And now if we go back to the terminal we can see that our nest application is starting up.
However it seems to be hanging.
And that's because currently I don't have MongoDB on my system running.
So I have MongoDB and my system already installed.
I will have another video that goes over how to install MongoDB.
If you do not already have it, we can easily start up the Mongo server by calling Mongo d dash dash
data path and then in this case, my data path where my data will be saved is a folder I created called
data slash db.
You can use whatever you want and once the MongoDB server is running, let's go back to our database
module here.
And if your application still has not connected to MongoDB, if mine hasn't yet, you can try changing
localhost here out for your actual localhost IP 127.0.0.1.
And now if we go back to our terminal, you can see that the mongoose module was successfully initialized
and our application successfully started, which means we have connected to the MongoDB database.
And if we even go back to the MongoDB logs, you can actually see that we accepted a connection on localhost.
And this is the port we accepted the connection from on our NodeJS app.
So now that we've successfully connected to our database, I want to go ahead and refactor how we're
actually providing this connection string to the mongoose module.
Instead of having to hard code this connection string, we want to expose this through an environment
variable, since this connection string will likely be different depending on where we're running the
application, and we want to be able to switch it out through an environment variable.
To do this, we can go ahead and create a dot env at the root of our project, which is a file that
will host all of our environment variables that our application will read in at runtime by default.
So I want to go ahead and specify a MongoDB Uri, which is a name that we'll create to specify the MongoDB
Uri and simply set it to the connection string.
So MongoDB 127.0.0.1 slash sleeper.
So this dot m file is automatically going to be loaded in by our config module, which uses the dot
env package internally to load this dot env.
Now we want to actually refactor this import to utilize the environment variable.
So to do this we'll instead call for route async which is going to allow us to actually inject dependencies
into a factory method.
So this means we can actually get access to the config service.
When we're initializing this mongoose module.
Now Nestjs will automatically make sure that the config service is ready.
By the time the mongoose module needs to be initialized, we simply just need to tell it which dependencies
it needs.
So let's go ahead and do that by first opening up an options object where we're going to go ahead and
specify that use factory property.
So this use factory is going to tell nest how to actually create the mongoose module options.
So this is going to be a function that takes in any injected providers.
So in this case we're going to inject the config service of type config service.
And we import this from nestjs config.
So as we know this is using env internally.
And we'll load that dot env file.
So now in this function we're going to go ahead and immediately return an object.
And this object is the mongoose module configuration which we had before that simply specified the Uri.
However now to get the Uri we can actually use the config service.
And we can call get on it to read in an environment variable.
And then we'll go ahead and pull off the Mongo DB Uri.
Now, finally, we need to tell Nestjs about these providers that we're injecting in the use factory
here.
So to do this we have another property after use factory called inject that we pass to for route async.
And this is the list of dependencies that we need available in order to actually execute this.
Use factory function here.
So we know this will be the config service from nestjs config.
So for root, a async is a very powerful pattern that Nestjs offers us, and it allows us to initialize
these modules asynchronously on application startup and get access to these dependencies like the config
service.
Now finally, we're actually going to need to make sure that we add an imports array to this as well,
and pass in the config module from our local config module, which we know is exporting the config service
for us.
Now next up I want to show you how we can implement validation on our environment variables so we can
tell nestjs about the environment variables we need when the application starts up, or else we can
throw an error.
So this way we always know that we have these environment variables ready that we need to actually run
the app.
In order to do this, I'll stop my development server and install a new package called Joy.
Which will allow us to do schema validation, and then we can go ahead and start our server back up.
And let's go back to our config module, where we can now provide an options object to the nest config
module here and add the validation schema.
And now in this case we can call Joy dot object because we're going to provide an object.
And the first environment variable that we've defined that we need is of course the MongoDB Uri.
So let's pass that in here we can see we get an error about importing joy.
We can resolve this by changing the import statement as import star as joy from joy.
And then now if we go back we can see that nest application starts successfully and we have no issues.
So we can set the MongoDB Uri here equal to joy dot string, which means that this has to be a string.
And we'll say that the value is required.
So now if we go back to the terminal we'll see.
We have no issues.
And our application is still running.
We were to then delete the MongoDB or from our file and then go back to our terminal.
We'll of course have to restart because by default it's not watching the dot env file.
We can see here that we have an error thrown a config validation error MongoDB Uri is required.
So we can see that we are now connecting to MongoDB and providing the connection string via an environment
variable.
Using our config module, we have successfully set up our database module, and we're now ready to start
building out our abstract repository that the rest of our microservices will use in order to implement
simple, reusable and maintainable data access.
Let's jump right in and I'll see you in the next lecture.