Node Guide

Menu
Menu

Node Guide

Setting Up an Node / Express / MongoDb Websever

You will need Node installed and have a MongoDb database set up.

Create a new directory for your project and navigate into it:

mkdir my-express-api cd my-express-api

Initialize a new Node.js project, accepting the defaults:

npm init -y

The above will set up the package.json configuration file.

Install the dependences express, mongodb and dotenv.

npm install express mongodb dotenv

Express is the web application framework that helps build the API, MongoDB is the database system used to store and retrieve data, and dotenv is a module used to load environment variables, which can include sensitive configuration information, into the application.

Creating the Entry Point

Create a file named index.js and open it in Visual Studio Code. This will be the entry point of your application.

Import the required modules and set up the basic Express server:

SNIPPET OF: index.js

Test the file with:

node index

The console should indicate the server is running on port 3000.

Run the Application with npm

To allow the app to run from npm instead of using the node command directly, you can update the scripts section in the package.json file. Follow these steps:

Open the package.json file in your project directory.

Locate the scripts section, which should look like this:

SNIPPET OF: package.json

Add a new script called "start" with the value "node index.js". Modify the scripts section to look like this:

SNIPPET OF: package.json

Save the changes to the package.json file.

Now you can run your application using the npm start command. It will execute the node index.js command defined in the "start" script.

Basic Route

In index.js add a basic response to a get. Place this before the code that starts the server:

SNIPPET OF: index.js

In the above the express server is set up to handle an HTTP GET request to the root URL ("/").

Let's break down the code:

  1. app.get('/'): This line specifies that the server should respond to a GET request made to the root URL ("/"). The app object represents the Express application and get is a method provided by Express to handle GET requests.

  2. (req, res) => { ... }: This is the callback function that will be executed when the server receives a GET request to the specified URL. The function takes two parameters: req and res. req represents the request object, which contains information about the incoming request such as headers, query parameters, and request body. res represents the response object, which is used to send the response back to the client.

  3. res.send('Hello World!'): Inside the callback function, this line sends the response back to the client. The send method is provided by Express and is used to send a string response. In this case, it sends the string "Hello World!" as the response.

So, when a client makes a GET request to the root URL ("/"), the server will execute the callback function and respond with the string "Hello World!". The response is then sent back to the client, completing the request-response cycle.

Setting up Routes

In Node.js and Express applications, it is common to have a file dedicated to managing routes. This file is often referred to as the "route file" or "route module." Managing routes in a separate file in Node.js and Express applications offers advantages in terms of code organization, modularity, reusability, testability, and scalability. It promotes clean and maintainable codebases, making it easier to develop, debug, and maintain your application's routing logic.

Create a folder named routes and a file named routes.js inside it. This file will handle the routing for your application.

In routes/routes.js, define a simple route that serves a "Hello, World!" message:

routes/routes.js

The route file does the following:

  1. First, the express module is imported using require('express'). This module allows you to create an instance of the Express application.

  2. The router object is created by calling the express.Router() method. This router object represents a mini application or a middleware that can be used to define routes and handle requests.

  3. The code defines a route for the HTTP GET request to the root URL ("/"). It uses the router.get() method to specify that when a GET request is made to the root URL, the provided callback function should be executed.

  4. Inside the callback function, the response object (res) is used to send the response back to the client. In this case, it sends the string "Hello, World!" as the response using the res.send() method.

  5. Finally, the router object is exported using module.exports = router. This makes the router object accessible to other parts of the application that import this file. It allows the router to be integrated into the main Express application by using app.use() or by requiring it in another file that manages the application's routes.

By exporting the router object, the routes defined in this file can be used in the main application file or in other modular route files. This separation of routes into a dedicated file helps maintain code organization and allows for better scalability and modularity in your Express application.

Import the Router into the App

Back in index.js, import the routes and register them with the Express app:

SNIPPET OF: index.js

Set up the Environmental Variables

To prevent database credentials been hardcoded we'll put them in the .env environmental variables file saved at the root of the application.

_env

These credentials can then be used to set up a database connection in a new file db.js. The above are for connecting to a local database. The details can be changed to connect to MongoAtlas for example:

_env

To ensure environmental variables are available to the application at the following to index.js.

SNIPPET OF: index.js

Set up the Database Connection

Create a new file db.js to hold the database connection logic.

db.js

The above code does the following:

  1. It imports the MongoClient class from the 'mongodb' module.
  2. It retrieves several environment variables related to the MongoDB connection, such as the host, username, password, port, and database name.
  3. It constructs the MongoDB connection URI using the retrieved environment variables. The URI is formed differently based on whether the MONGO_LOCAL variable is set to 'true' or not. If MONGO_LOCAL is 'true', the URI will use a local connection string (mongodb://${MONGO_HOST}:${MONGO_PORT}/${MONGO_DBNAME}). Otherwise, it will use a connection string for MongoDB Atlas (mongodb+srv://<username>:<password>@<host>/<dbname>?authSource=admin).
  4. It creates a new instance of MongoClient using the constructed MONGO_URI.
  5. It retrieves the MongoDB database from the client using the db() method.
  6. It exports the client and db objects so that other parts of the application can use them for performing database operations.

In summary, this code sets up a connection to a MongoDB database by creating a MongoClient instance and retrieving the database object. The connection URI is constructed based on the provided environment variables, and the connection can be either local or through MongoDB Atlas depending on the value of the MONGO_LOCAL variable.

Controllers

Controllers are responsible for handling business logic and like with routes we'll separate them out. By separating the application logic into different folders, such as controllers and routes, you achieve better code modularity and separation of concerns.

Create a folder named controllers and a file named controllers.js inside it. This file will contain the logic for querying the MongoDB collection and will be called via routing later in our example.

controllers/controllers.js

Here's a breakdown of what the code does:

  1. It imports the necessary modules, specifically the db object from the '../db' module. This module contains the MongoDB connection and database object.
  2. It assigns the 'filmsCollection' collection from the db object to the collection variable. This assumes that the 'filmsCollection' collection exists in the connected MongoDB database.
  3. It defines an asynchronous function called getAllData().
  4. Within the try block, it queries the collection using the find() method, which retrieves all documents in the collection. The toArray() method is then called on the resulting cursor to convert the retrieved documents into an array.
  5. The retrieved films array is returned from the function.
  6. If an error occurs during the retrieval process, it is caught in the catch block. The error is logged to the console, and an object with an 'error' property is returned indicating that the data retrieval failed.
  7. Finally, the getAllData() function is exported as an object with the key getAllData using module.exports.

In summary, this code defines a controller function that interacts with a MongoDB collection to retrieve all documents. It uses the collection object obtained from the db module, performs a query to retrieve the documents, and returns them. If any error occurs, it logs the error and returns an object indicating the failure. The function is then exported for use in other parts of the application.

Try/Catch and Async/Await

The async and await keywords are used in this code to handle asynchronous operations. Here's why they are necessary:

  1. The collection.find().toArray() method used to retrieve all documents from the collection returns a promise. Promises are a way to handle asynchronous operations in JavaScript, but they can be cumbersome to work with due to callback chaining or using .then().

  2. By marking the getAllData() function as async, it allows the use of await inside the function. await is used to pause the execution of the function until the promise is resolved, and it provides a more readable and synchronous-like code structure.

  3. Inside the try block, await collection.find().toArray() is used to wait for the retrieval of all documents from the collection. The execution of the function is paused until the promise is resolved, and the result is stored in the films variable.

  4. If the promise is resolved successfully, the films array is returned from the function. Since await is used, the actual returned value will be the resolved value of the promise (in this case, the films array).

  5. If an error occurs during the promise resolution, it is caught in the catch block. This allows for proper error handling. In this case, the error is logged to the console using console.error() and an object with an 'error' property is returned to indicate the failure.

Using async and await simplifies the code structure by avoiding nested callbacks or long chains of .then(). It allows for a more sequential and readable flow, making it easier to handle asynchronous operations in a synchronous-like manner.

Add a call to the controller in the router

Extend routes.js to call the controller and access the data.

SNIPPET OF: /routes/routes.js

The above imports the getAllData function from the controllers module and assigns it to the dataController variable. This function is responsible for retrieving data.

When data is retrieved it is sent to the response object with in JSON format using res.json(data).

Save and test your files to see the data retrieved against the URL localhost:3000/api/

Add a Controller to find a Document by id

We are also likely to want to retrieve an individual document. The easiest way to do this is by _id. We will pass this value as a URL parameter such as http://localhost:3000/api/645268083f37a8fd938b26a1

The following getDataById function to routes.js.

routes/routes.js

The role of ObjectId in MongoDB is to provide a unique identifier for documents stored in a collection. MongoDB automatically generates and assigns an _id field with an ObjectId value to each document if one is not provided explicitly. It serves as a primary key and ensures uniqueness within the collection. In the added code, the getDataById function uses ObjectId to convert the provided id parameter into a valid MongoDB ObjectID and then uses it to query the collection to retrieve the document with a matching _id field.

This will require the ObjectId module from the 'mongodb' package. ObjectId is a constructor function provided by the MongoDB driver for Node.js, and it is used to create valid ObjectIDs.

As such add the following to the top of the file:

SNIPPET: routes/routes.js

We also need to export the new getDataById function, so add to the module.exports at the nd of the file:

SNIPPET: routes/routes.js

Add id route

Add a new route to the router.js file.

The :id syntax is used to pass a parameter as part of the request object get call.

This is retrieved with req.params.id and pass as a parameter to the getDataById function in the controller created above.

SNIPPET: routes/routes.js

Test

Start the server and test your API!

npm start

You should now have a Node/Express application set up with a basic "Hello, World!" route as well as routes to list all data and fetch a document based on its _id. Feel free to customize the code according to your specific needs and expand it to handle more routes and controllers.

Tip: This Repo will have the code from the above.

Next: REST Endpoints for CRUDing