Creating a REST API in Node.js With Express, TypeScript, MongoDB and Docker | Better Programming

Let the coding begin

Setting up our server

The first thing we are going to do is create our basic directory structure. We are going to create a directory named src, which will contain all of our project files, aside from config:

mkdir src
cd src
mkdir constants
touch server.ts
touch app.ts

Let us open the app.ts file we just created, which will contain our express configuration. This may seem like a lot of code all at once, but don’t worry! It’s just the basic configuration that express needs to work, and we’ll go over every single line so that we understand what’s going on:

Let’s go over Express’ config quickly:

  • Body parser allows us to receive requests with data in different formats, such as json, or x-www-form-urlencoded.
  • CORS (Cross-Origin Resource Sharing) uses additional HTTP headers which let our browser know that it has to allow a web application running at one domain to access resources from a server at a different origin.

Once this is done, we’re going to create a file to store our app’s constants. Why? Because this way we only have to declare each constant once. Whenever we need to make use of it, we just have to import it.

Furthermore, if we need to change the value of our constant (yes, even though it’s a constant, the time may come when we need to change its value), it will change everywhere in our project, since it’s only declared in one place. All of this said, let’s create our constants file:

cd constants
touch pokeApi.constants.ts

The first constant we are going to declare is our PORT, which will store the number of the port we are going to open for our server:

//src/constants/pokeApi.constants.ts

export const PORT = 9001;

Now, head over to our server.ts file, where we will set up our server:

Tip: If anyone is unfamiliar with the syntax I’m using in the console.log, it’s a technique named template literals. You type everything inside grave quotes (), and use interpolation (${}) to embed variables. More about this technique here.

Note that we are importing both the app we created previously, and our PORT constant.

And with just these three lil’ files, we’ve created our very own server! Fire up your terminal and execute the npm start script we created previously. You can do this by typing:

npm run start (or just npm start for the pros)

Tip: Since we are using nodemon to watch our project files, we only need to execute the previous command once. Every time we save our changes, nodemon will automatically restart our app for us.

After executing the command, you should see the Listening on port 9001 message on your terminal. Awesome! We now have our server up and running.

You can also head over to your favorite browser to check it out:

localhost:9001

You should see a message similar to this: Cannot GET /. I know, not very exciting… But if you’re seeing this message, it works! If not, go back and re-check your code to make sure nothing is missing.

Creating our first GET route

Since we now have our server up and running, we are going to create the first GET route and display a nice welcome message. After all, ‘Cannot GET /’ isn’t exactly welcoming.

To do this, we need to create a file named pokemon.controller.ts (in src), which will have a public router property. This property will hold an instance of the Express router, which we will use to define our routes. Here’s what our controller should initially look like:

Our Pokemon controller is where we’re going to define all of the Pokemon routes. Each route will perform a different action, like for example:

  • Getting a list of all the available Pokemon in the database.
  • Adding a new Pokemon.
  • Updating a Pokemon.
  • Deleting a Pokemon.
  • Anything else we want to do.

The first route that we are going to define is a simple GET route, which will return a nice welcome message. To do so, we need to create a setRoutes method, which will be responsible for defining the routes. Inside it, we can then define our first GET route, like so:

Tip: Don’t forget to call the setRoutes method in the controller, or the routes won’t work!

As you can see in the previous code, we’ve used the Express router to define a GET route, which receives two parameters:

  • The first is the path we’ll use to access our route, which we’ve defined as /.
  • The second is a callback function, which in turn receives two parameters (it can receive an optional third parameter, next, which we will use later on in our other routes): the Request and Response objects. The Request object, as its name indicates, represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers etc. On the other hand, the Response object represents the HTTP response that our app will send out to answer any received HTTP requests.

Therefore, by using the Response object we can send out our welcome message. Simple, isn’t it? However, there is something that is slightly off about our route…

The separation of concerns

The main goal that developers should have is to write clean, maintainable code. Why? Because

Our code should not depend on concrete implementations. What does this mean exactly? Allow to me to illustrate with an example:

For this project, we are, initially, going to use a MongoDB database. However, let’s suppose that we are required to change the database to MySQL. Since all of the functions that interact with the database are defined inside the controller, alongside the routes, to make the change to a MySQL database we would have to go to our controller, and modify every single function.

However, if we define all of the MongoDB database functions in a separate file, a file named PokemonService that implements a generic , if we wanted to change to MySQL, all we would have to do is create another service, this time with a MySQL specific implementation of the generic interface, and inject said service into our controller, substituting the MongoDB service.

In a nutshell, by using a service to interact with our database, we keep our code loosely coupled, ergo maintainable.

Creating the Pokemon service

Therefore, we will now create a directory named services, and inside it, we’ll create a file named pokemon.service.ts. Once we’ve done that, we’ll add the following code:

PokemonService version 1

A very simple function, which returns our cute welcome message. Now, we need to head over to our pokemon.controller and do two things:

  1. Import the service we have just created.
  2. Call the service’s getWelcomeMessage function from our main GET route.

As you can see, our main route now calls the getWelcomeMessage function that we have just created in our PokemonService, and sends it out by using the Response object.

So far, so good. It’s time to import our Controller into our app.ts:

As you can see on line 28, we’re once again making use of the app.use method, which receives an optional path argument as well as the middleware callback function. Basically, what we’re doing here is telling Express to invoke the PokemonController router whenever we make a request that begins with /pokemon.

And we’re done! Head over to your browser, and if you’ve done everything correctly, you should see your welcome message displayed, like this:

Welcome message