This is part 2 of a series of posts which will show you how to create a RESTful API in Node.js. For further reading please check out the following links:
- PokeAPI REST in NodeJS with Express, TypeScript, MongoDB and Docker — Part 1
- PokeAPI REST in NodeJS with Express, TypeScript, MongoDB and Docker — Part 3
- If you prefer to check out the full code, you can find the full PokeApi project here.
In the previous post, we set up our server and implemented our first GET route, which returned a lovely welcome message. As our goal is to perform basic CRUD operations on our Pokemon data, we need to have a database to store our information.
Once again, before we start, a little preview of how our directory tree will look by the end of this post:
Just as a reminder: to run our project we’re currently using the following command:
npm run start
This said, let us begin.
Creating Our Docker-Compose File
The first thing we are going to do is create a
docker-compose.yml file, on the same level as our
src directory, which is to say, outside the
Once this is done, copy and paste the following code into the newly created file:
I’ll briefly explain what these configuration options mean:
version: Specifies the docker-compose version we are going to use.
services: We can specify a list of services which will be deployed with our container. In our case, we want a database, which is why we use the following attribute:
db: We indicate that we are going to deploy a database.
container_name: This is optional. It allows us to specify a custom container name. If we omit this option, a default container name will be generated.
image: Specifies the image that the container will be built from. In our case, the latest MongoDB image.
restart: Always restart the container if it stops. If it is manually stopped, it is restarted only when Docker daemon restarts or the container itself is manually restarted.
volumes: This is a very interesting option. It allows us to have data persistence. What does this mean? All of our data is going to be stored in a Docker container. However, Docker containers can be stopped, restarted, etc. In such cases, what happens to our data? Does it disappear? The answer is, it won’t disappear if we use the volumes option. We can specify a directory on our local machine where our data will be stored. In our case, this directory is named
environment: We can specify environment variables. In our case, we are creating a database named Pokemon when the container starts.
ports: Specifies the ports that will be exposed (Host port: Container port). In our case, we are mapping our local port 27017 to the container port 27017 (27017 is the default port for MongoDB).
For more information about docker-compose files, you can check out this link.
Now that we have our docker-compose file ready, let’s start the container.
Fire up your terminal, and type this command:
If you’ve done everything correctly, you should be seeing output similar to this on your terminal:
You should also see that a new directory named
pokeData has appeared in your directory tree. This is the directory we specified earlier in our docker-compose file, by using the “volumes” attribute.
pokeData directory will store all our database data (once we insert some), and keep it safe and sound.
Isn’t Docker awesome and easy to use? A simple, intuitive, extensively documented configuration file and one command are all we need to have our database instance up and running. Beautiful.
If you’re getting the following output after executing the
docker-compose up command:
ERROR: Couldn’t connect to Docker daemon at http+docker://localhost — is it running?
It means that your Docker daemon isn’t running. Execute this command to start the Docker daemon:
sudo systemctl start docker
And try the
docker-compose up command again. The error should be gone.
Connecting Our App to Our Dockerized MongoDB Database
We have our database container deployed and running, so we now need to connect our application to it.
app.ts file, and add the following code:
You may have noticed that, once again, we are hard-coding a variable: the Mongoose connection string.
To avoid this, let’s open our
constants file, and store it there:
//src/constants/pokeAPI.constants.tsexport const PORT = 9001;
export const WELCOME_MESSAGE = "Welcome to pokeAPI REST by Nya ^^";
export const MONGO_URL = "mongodb://localhost:27017/Pokemon";
Back in our
app.ts, we can now change the hard-coded string for our newly defined constant:
If we’ve done everything correctly, we should now be seeing the output below in our terminal where we ran our
docker-compose up command.
If, for any reason, you stopped docker-compose previously, run the command again.
As you can see, our Docker container has accepted the connection we made from our application. So far, so good.
Creating Our Data Model
Now that we are connected to our database, we need a way to interact with it.
To achieve this, we are going to use Mongoose, which provides us with several data modeling tools, such as schemas and models. Mongoose makes interacting with MongoDB exceedingly easy and simple.
“Models are fancy constructors compiled from schema definitions. An instance of a model is called a document. Models are responsible for creating and reading documents from the underlying MongoDB database.” — Mongoose documentation
To store our data models, we are going to create a models directory in
src, which will contain a file named
Inside this file, we are going to import Mongoose and create our data model:
Once we’ve created our Pokemon schema, we need to create a Mongoose model.
To do this, we will part from our newly created schema. Therefore, in the same file:
Note: I am fully aware of the fact that we are defining both our schema and model in the same file, a file named
Despite being a control freak, I refuse to create two separate files for 10 lines of code. If you are even more obsessed than I am, you may, of course, create a separate file for your schema.
With our Pokemon model just created, it is now time to import it in the
The Pokemon model will later be used to query our MongoDB database, once we create the CRUD routes and their respective
db query functions.
This, however, we will leave for the following post.
In this post, we’ve learned how to deploy an instance of MongoDB with docker-compose, and how to connect our application to it. We’ve also used Mongoose to create both a schema and a model for our database.
If you’d like to see the full code for this post, you can do so here (branch “part2” of the PokeAPI project).
Thank you so much for reading, I hope you both enjoyed and found this post useful.
In the following post, we will be implementing the rest of the routes that are necessary to create a basic CRUD, as well as their respective database query functions.
Here is the link to the next post: