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:
Initialize a new Node.js project, accepting the defaults:
The above will set up the package.json
configuration file.
Install the dependences express, mongodb and 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:
Test the file with:
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:
Add a new script called "start"
with the value "node index.js"
. Modify the scripts
section to look like this:
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:
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:
app.get('/')
: This line specifies that the server should respond to a GET request made to the root URL ("/"). Theapp
object represents the Express application andget
is a method provided by Express to handle GET requests.(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
andres
.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.res.send('Hello World!')
: Inside the callback function, this line sends the response back to the client. Thesend
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:
The route file does the following:
First, the
express
module is imported usingrequire('express')
. This module allows you to create an instance of the Express application.The
router
object is created by calling theexpress.Router()
method. Thisrouter
object represents a mini application or a middleware that can be used to define routes and handle requests.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.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 theres.send()
method.Finally, the
router
object is exported usingmodule.exports = router
. This makes therouter
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 usingapp.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:
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.
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:
To ensure environmental variables are available to the application at the following to index.js
.
Set up the Database Connection
Create a new file db.js
to hold the database connection logic.
The above code does the following:
- It imports the
MongoClient
class from the 'mongodb' module. - It retrieves several environment variables related to the MongoDB connection, such as the host, username, password, port, and database name.
- 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. IfMONGO_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
). - It creates a new instance of
MongoClient
using the constructedMONGO_URI
. - It retrieves the MongoDB database from the client using the
db()
method. - It exports the
client
anddb
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.
Here's a breakdown of what the code does:
- It imports the necessary modules, specifically the
db
object from the '../db' module. This module contains the MongoDB connection and database object. - It assigns the 'filmsCollection' collection from the
db
object to thecollection
variable. This assumes that the 'filmsCollection' collection exists in the connected MongoDB database. - It defines an asynchronous function called
getAllData()
. - Within the
try
block, it queries thecollection
using thefind()
method, which retrieves all documents in the collection. ThetoArray()
method is then called on the resulting cursor to convert the retrieved documents into an array. - The retrieved
films
array is returned from the function. - 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. - Finally, the
getAllData()
function is exported as an object with the keygetAllData
usingmodule.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:
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()
.By marking the
getAllData()
function asasync
, it allows the use ofawait
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.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 thefilms
variable.If the promise is resolved successfully, the
films
array is returned from the function. Sinceawait
is used, the actual returned value will be the resolved value of the promise (in this case, thefilms
array).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 usingconsole.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.
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
.
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:
We also need to export the new getDataById
function, so add to the module.exports at the nd of the file:
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.
Test
Start the server and test your API!
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.