So with that, we added these two features
to the database, MongoDB container.
Now, let's work on the node backend,
and there we also wanna ensure that data persists
in this case, the log files, this container writes,
and we wanna have life source code updates
so that when we change our source code
in our local host project folder,
the latest source code is also available in the container.
For this, I'll first of all,
stop my goals backend container again.
So this node container, so that we can restart it
with a different configuration
because we now also wanna add a volume there,
or actually two volumes.
We wanna run our goal backend with docker run,
but we need volumes.
We need one volume for the log files.
Now we can go for a name volume to not know
where they are stored on the host machine,
but to ensure that they survive container tear down,
or we go for a bind mount so that we can also read
the log files from inside our hosting machine.
Now you could do either way.
I'll go for a named module here,
so that we don't have easy access to these files
from the host machine,
but just to practice using volumes again.
And for that, I'll name this logs
and bind this to /app/logs in the container.
/app is our working directory
as specified in the docker file.
So that is where the entire application is copied to.
And then we have the logs folder in there
to which this application writes the logs
and therefore, it's /app/logs internally
in the container where logs are written to
and that's there for the path we wanna bind to our volume.
Now that's one volume, but we need another one,
or to be precise, we need a bind mount
which we also create with /v build.
I wanna bind everything in the app folder
to my local hosting directory here,
simply to ensure that whenever I change something
in my source code here,
it's reflected inside of the container.
Now for such a bind mount,
you need the full path to your folder here,
which you bind as a name to the container internal path.
So, therefore, here, we can, for example, right-lick
on app.js and copy the path and paste this in here.
However, delete app.js at the end.
So that I just paste the full path to backend
and bind this to the internal app folder.
Now also keep in mind what you learned about volumes
and files overwriting each other in the container.
Longer container internal paths have precedents
and overwrite shorter paths.
So in the example of logs here,
we already do ensure that logs written by the container
are not overwritten by the local logs folder
in my local host project folder.
So that's good. The logs in the container will survive.
But we also learned that we should do something similar
about node modules to ensure that if we didn't have
node modules here on the local host machine,
we don't overwrite the existing node modules
in the container with the non-existing ones here,
which would cause our container to crash
because dependencies would be missing.
So therefore we should add another volume,
and here we can simply use an anonymous one
and point at app node_modules
to basically tell the container
that the existing node modules folder
inside of the container should stay there
and should not be overwritten
by the non-existing node modules folder
in our host machine folder, which we bind to the app folder.
And I did talk about this in the volumes course section.
So definitely make sure you go through that again
in case this doesn't tell you anything.
With that though, we should be starting
this node server here with our volumes added,
and I'm again, going to run it in detached mode now
and start it.
And it is up and running. So that's looking good.
If I reload my application, my React application here,
it also is still able to fetch data,
and deleting all the words and adding again all the words,
so that also is looking good.
All is got no errors here in the developer tools.
And that proves that indeed our containers
are up and running successfully.
Now, although the node container with our bind mount
and the upper volumes, however, we're not done yet.
Yes, we do put our latest source code
from the local host folder into the node container,
but currently that doesn't do much
because when we start the container,
we run app.js with the node command,
and that basically logs in the code
at the point of time this containers starts.
The node process loads all the code,
and then starts that code, so to say.
So, if the code changes their author,
this has no impact on the already running node server.
And that's of course not what we want here.
We want the node server to restart
whenever the code changes.
Yes, we could stop and restart the container,
that would do the trick, but even better than that,
we can add the extra dependency to this project,
which then actually will restart the server
automatically for us when the code changes.
For this, I'll delete package log JSON,
and here in packaged.js, I'll add a new devDependencies node
below dependencies, and add nodemon here as a dependency,
and I'll pick version 2.0.4.
Nodemon is an extra tool,
which I already covered early in the course,
which watches the project folder for file changes
in JavaScript files,
and if such a JavaScript file does change,
it restarts the node server.
We just have to ensure that to utilize nodemon,
we can do that by adding a start script here
to the scripts section.
And there, I wanna run nodemon app.js
to use nodemon to run app.js,
and under the hood nodemon uses node,
but it restarts the node server whenever any JavaScript file
in the project folder,
so in this case, in the backend folder changes.
So these are changes we need to make to package.js,
in the docker file of the backend project,
we now need to run this start script,
and can start like this to utilize nodemon.
And that of course now means that we need to stop
our goals backend container,
so there's node application container we started,
and then restart it, but based on a rebuilt image
because we changed the docker file.
So we built this goals node image again,
and it installs all new dependencies ends on
because the package.js file changed
and thereafter, we can run this container
with the same configuration as before.
And if you'd run docker, PS,
it should be up and running, and it is,
but now it uses nodemon internally.
And we can verify this by running docker logs goals-backend,
which was our container name.
Here, we see the nodemon log.
And now if we change something in our code here,
for example, here, when we say connected to MongoDB,
I add two exclamation marks.
If I save this and we log our goals back in logs again,
we see it restarted because of changes,
and we see that latest log
and our application also still works.
So now we also successfully provide life source code updates
to our node application with help of our bind mount.
And in this case with help of nodemon,
which watches for file changes and restarts the node server
without us being required to manually restart the container,
which would be an option, but of course is less convenient.
Now, there are two last things which I wanna tweak
about this node application.
One is this connection string.
I had coded username and password into this string here,
but of course that means that whenever I change that
when I start my MongoDB container,
so if I run MongoDB here with a different username
or a different secret,
which definitely is something that could happen,
I need to change it in my code here, which is not horrible,
but we could utilize environment variables here
to inject dynamic values here and actually set
the username and password we do use
when we start this node container.
And that's something I wanna do here.
For data and the docker file,
I'll add environment variables with the nth instruction,
and I'll add one which will name MONGODB_USERNAME.
The name is totally up to you here,
and I'll set this to a default value of root,
and add a second one, which I'll name MONGODB_PASSWORD,
which I'll set to secret as a default value.
Now these environment variables are available
in the container environment and also in the running code.
Now in app.js, we can utilize that.
I wanna replace max with the values stored
in the username environment variable
and use the password environment variable to get
to the value for the password and disconnection string.
To conveniently add this in JavaScript,
we can replace these single quote here with a backtick,
which is a different character on your keyboard
and do that all at the end of the string here.
And that's just a special JavaScript syntax,
has nothing to do with docker,
which makes injecting dynamic values into a string easier.
Now we can add dollar sign curly braces here,
and then add a dynamically resolved value here,
which is then combined into one long string.
And here it's process.env, and then MONGODB_USERNAME.
And here you should make sure that you really use
the exact same environment variable
as you specified in docker file,
and do the same here for the password,
dollar sign curly braces, which is just JavaScript syntax,
has nothing to do with docker and then process.env,
and then here it's MONGODB_PASSWORD,
which I wanna access.
And now these values will be injected dynamically,
and we can set them when the container starts.
So therefore here, I'll now stop my goals backend container,
this node application container,
and then I wanna restart it with specific values passed
for these environment variables.
The default values will not work because of root and secret
are not the username and password I chose
on my MongoDB database.
There, I did choose max as a username, not root.
So therefore, when I read on this container now,
I will add an environment variable here, for example,
with -d, and set MONGODB_USERNAME equal to max.
We could also set MONGODB_PASSWORD to a different value,
but here in my case,
the default value I assigned will actually work.
And of course I could have also used max
as a default value here,
and we wouldn't need to set any environment variable
at all then, but I also want to practice again
how we set our own environment variables.
So with that, this should not work.
And if we inspect the logs of this container,
we see authentication failed.
And the reason for that can be found in the docker file.
We should assign a default value like this
with an equal sign.
My bad.
We should add the default value like this
with an equal sign.
And if we do that, we can now stop the goals backend,
so this running container,
and since we changed the docker file, again,
rebuild the image to pick up this latest docker file.
So rebuild the image with docker build goals-node
and then run the container again
with our MONGODB_USERNAME being set, but not the password.
And if we do this,
we can again, look into the logs here,
and now you see this connected.
And hence, if I reload here,
this application also again works.
So that is working here, and that's how we can add
our own environment variables.
Now with the environment variables out of the way,
there is one last thing I wanna take care of.
At the moment, thanks to the stalker file,
I copied everything from this backend folder
into the container, after the dependencies
have been installed in there.
This is okay, but there are files I'm copying
into the container, which I might not wanna copy in here.
And for that, I'll add a .dockerignore file.
I also covered this earlier in the course already.
Here, I, for example, want to exclude node modules.
If I had this folder here,
I'll also exclude the docker file itself
and exclude a .git folder if it should exist.
And these are some folders which are now simply won't copy
into the existing container,
and the node modules folder is the most important one
simply to ensure that I'm not unnecessarily copying
all my dependencies again after I already did install them
inside of the container.
And that is now really all.
Now, if we would rebuild the image,
we would ensure that the image gets built
without these redundant files being copied in.
And we now have our node application
where we can change the code
and it will be picked up automatically
and where we also use environment variables.
So therefore, let's now move on to the React application.