Share
Explore

Lab 01 MONGOOSE API


The tooling Setup :

Node.js, NPM, Access to
Install your MONGO DB Server and sure it is running (services panel)

Lab Lesson Worksheet: Mongoose API with Node.js in Visual Studio Code

Objective

At the end of this lab session, you should be able to:
Set up a Node.js environment in Visual Studio Code
Connect to MongoDB using Mongoose API
Perform CRUD operations on a simple Mongo database for a Warehouse

Prerequisites

Visual Studio Code installed on your computer
Node.js and npm (Node Package Manager) installed
Basic understanding of JavaScript

Tools and Resources

Visual Studio Code (VSCode)
Node.js (https://nodejs.org/)
MongoDB Atlas account or a local MongoDB installation (https://www.mongodb.com/cloud/atlas)
Mongoose Library

Part 1: Setup

Step 1: Create a New Node.js Project

Open Visual Studio Code and create a new folder for your project called warehouse.
Open a new terminal in VSCode and navigate to the warehouse folder.
Initialize your Node.js project with npm init -y.

Step 2: Install Dependencies

In your VSCode terminal, type and execute npm install express mongoose dotenv --save. This will install Express, Mongoose, and the dotenv library to manage environment variables.
Dotenv is a zero-dependency module that loads environment variables from a .env file into . Storing configuration in the environment separate from code is based on methodology.

Step 3: Connect to MongoDB

Create a .env file in your project root directory and add:
MONGODB_URI=<Your_MongoDB_Connection_String>
Replace <Your_MongoDB_Connection_String> with your actual connection string provided by MongoDB Atlas or your local MongoDB instance.

Step 4: Initialize Mongoose Connection

Create a new file app.js in the root directory of your project.

In app.js, add the following code to set up the connection to MongoDB using Mongoose:

require('dotenv').config();
const mongoose = require('mongoose');
const express = require('express');
const app = express();

const PORT = process.env.PORT || 3000;

mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log("We're connected to the database!");
});

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

Part 2: Define the Schema and Model

Step 1: Create a Warehouse Item Schema

In the app.js, define a Warehouse item schema using Mongoose:
const itemSchema = new mongoose.Schema({
itemName: String,
quantity: Number,
category: String,
location: String,
lastUpdated: { type: Date, default: Date.now }
});

Let's go into detail about how to define Schemas and Models in Mongoose, which is a popular Object Data Modeling (ODM) library for MongoDB and Node.js.

I'll make sure to explain how this process works to build up the documents and collections in the MongoDB database.

Step 1: Define a Warehouse Item Schema

Mongoose provides a way to define the structure of the data that will be stored in MongoDB through the concept of schemas.
A schema is essentially a blueprint for our data - it defines the shape of documents within a collection. {Think of class that we make objects from.}.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const itemSchema = new Schema({
itemName: String,
quantity: Number,
category: String,
location: String,
lastUpdated: { type: Date, default: Date.now }
});
Here's what's happening in the schema definition:
mongoose.Schema: This is a constructor function that you use to define the structure and rules of your data. Schema is destructured from mongoose.
itemName: It specifies that the itemName attribute of our document will be of type String.
quantity: This attribute is defined as a Number and will represent the quantity of an item.
category: Another string that will describe the category of the item.
location: The location of the item in the warehouse, also a string.
lastUpdated: This is an object that defines not only the type (Date) but also a default value. If lastUpdated is not provided when creating a document, it will automatically be set to the current date and time (Date.now).
By using schemas, we can also define validators, default values, middleware, virtual properties, etc.
This schema doesn't just define the structure, but it also implicitly applies validation rules to the data.

Step 2: Create a Model from the Schema

Once a schema is defined, we can create a Model from it.
Models are JavaScript classes that you define from which you create instances that correspond to MongoDB documents (OBJECTS).
MODELS are constructor functions that take a schema (class) and create an instance of a document equivalent to records [rowsets] in a relational database.
const Item = mongoose.model('Item', itemSchema);
mongoose.model: This function takes two arguments.
The first one is the singular name of the collection your model is for. Mongoose automatically looks for the plural version of your model name. So, for instance, if your model is Item, it will look for items in the database.
The second argument is the schema you want to use for that model.
Item: This is the model class we've created. From now on, we can use Item to create instances of our Warehouse Item documents.

How It Builds the Documents and Collections in MongoDB Database

After defining the schema and model, here's how Mongoose interacts with MongoDB to build the documents and collections:
Creating and Saving Documents: Whenever you want to create a new warehouse item, you instantiate a new document by calling new Item({ /* properties */ }) where the properties object matches the shape of itemSchema. Calling save on the instance persists it to the MongoDB database.
Collection Creation: If the collection does not already exist in the database, MongoDB will automatically create the collection the first time you insert a document into it.
Schema Enforcement: When you attempt to save a document, Mongoose validates it against the schema you've defined. If any fields are missing or contain the wrong type of data, Mongoose will throw a validation error.
Querying Documents: With a Mongoose model, you can perform queries on the collection. Mongoose documents can be retrieved using the model's find, findById, findOne, or similar methods and then manipulated or sent to clients.
Middleware (Hooks): Schemas can have pre and post hooks for various operations. For example, you can add a pre-save hook to your schema to perform certain actions before a document is saved to the database.
By utilizing schemas and models, Mongoose simplifies the interactions with MongoDB databases. It allows you to handle data as structured objects with a predefined schema that can integrate business logic, making development more intuitive and efficient.

Step 2: Create a Model from the Schema

Create a model for the schema:
const Item = mongoose.model('Item', itemSchema);

Part 3: Express.js Setup for Handling Requests

Add middleware to parse JSON:
app.use(express.json());

Part 4: CRUD Operations

Now you will define API routes to perform CRUD operations.

Step 1: CREATE Items

app.post('/items', async (req, res) => {
try {
const newItem = new Item(req.body);
const result = await newItem.save();
res.status(201).send(result);
} catch (error) {
res.status(400).send(error);
}
});

Step 2: READ Items


app.get('/items', async (req, res) => {
try {
const items = await Item.find({});
res.status(200).send(items);
} catch (error) {
res.status(500).send(error);
}
});

Step 3: READ Filtered Items

Assuming we want to filter items based on a category:

app.get('/items/:category', async (req, res) => {
try {
const items = await Item.find({ category: req.params.category });
res.status(200).send(items);
} catch (error) {
res.status(500).send(error);
}
});

Step 4: UPDATE Items


app.put('/items/:id', async (req, res) => {
try {
const updatedItem = await Item.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.status(200).send(updatedItem);
} catch (error) {
res.status(400).send(error);
}
});

Step 5: DELETE Items


app.delete('/items/:id', async (req, res) => {
try {
await Item.findByIdAndDelete(req.params.id);
res.status(204).send(null); // No content
} catch (error) {
res.status(500).send(error);
}
});

Part 5: Test Your API

Run your Node.js application with node app.js and test each endpoint using a REST client like Postman or VSCode REST Client.

Deliverable

Submit your app.js containing the code for setting up the server, connecting to MongoDB, and performing CRUD operations.

Notes

Ensure error handling is robust to manage common issues that might arise during CRUD operations.
Always test your connections and handle promises/async operations to prevent server crashes.
Take your time to understand each step and consult documentation or ask for help when you encounter difficulties. Good luck!

Explain what a schema is and the need for a schema


A schema, in the context of a database, is a structured blueprint or a set of rules that defines how data is organized, related, and stored within a database. It specifies the structure of the data that the database will hold, often including definitions for tables, fields, relationships, indexes, and data types. In more technical terms, it represents the formal structure of how data is represented in a database system.
In MongoDB with Mongoose (which is an Object Data Modeling (ODM) library for MongoDB and Node.js), a schema is used to define the structure of the data and model the documents within a MongoDB collection. A schema in Mongoose declares the fields stored in each document along with their validation requirements and data types, such as strings, numbers, dates, or even complex nested objects.
Here are some key reasons for using schemas:

Data Consistency and Validation

Schemas provide a means to define the allowable shape of documents in a collection. They can ensure that each entry conforms to a defined structure and adheres to the types of data specified for each field (for example, strings, numbers, booleans, etc.). Validation rules can also be specified, such as required fields, default values, or custom validation functions, to maintain the integrity and quality of the data.

Application Reliability

By enforcing a consistent structure for data, schemas help maintain the reliability of an application. Developers can trust that the data they are working with is in the expected format, which reduces the chances of runtime errors.

Data Modeling and Relationships

Schemas can define not only the individual fields but also the relationships between different pieces of data. With Mongoose, for instance, you can use schema features like references (ref) to create associations between different documents, similar to foreign keys in relational databases. This capability makes it easier to model complex data structures and manage related data.

Organization and Readability

They make it easier to understand the expected structure of the data within the database for anyone who looks at the codebase or works with the database. It serves as a form of documentation that can speed up development and prevent mistakes.

Query Optimization

Schemas can also be used to define indexes, which optimize the database's ability to perform search queries. By specifying indexes in the schema, we can improve the speed and performance of database operations.

Code Maintenance and Scalability

As the application grows in complexity, the schema becomes even more crucial. It helps in managing data across different parts of the application and eases the process of updating the database structure.
In summary, schemas are pivotal in database design for ensuring the quality, integrity, and consistency of the data. They help developers and systems to properly interact with the database in a structured and predictable manner, leading to more reliable and maintainable applications.

Complete working application:

Below is the complete app.js file incorporating Express.js along with all the necessary setup for a basic application using Mongoose to interact with a MongoDB database.

You will need to have Node.js and npm installed to run this application, along with MongoDB accessible either locally or remotely via a connection string.

Make sure you also have the dotenv, mongoose, and express packages installed in your node project.

require('dotenv').config();
const mongoose = require('mongoose');
const express = require('express');
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;

// Establish a database connection
mongoose.connect('mongodb://localhost:27017', { useNewUrlParser: true, useUnifiedTopology: true });

// Database connection events
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log("We're connected to the database!");
});

// Define a warehouse item schema
const itemSchema = new mongoose.Schema({
itemName: String,
quantity: Number,
category: String,
location: String,
lastUpdated: { type: Date, default: Date.now }
});

// Create a model from the schema
const Item = mongoose.model('Item', itemSchema);

// Set up routes

// Create a POST route to add a new item
app.post('/items', async (req, res) => {
try {
const newItem = new Item(req.body);
const result = await newItem.save();
res.status(201).send(result);
} catch (error) {
res.status(500).send(error.message);
}
});

// Create a GET route to fetch all items
app.get('/items', async (req, res) => {
try {
const items = await Item.find({});
res.status(200).send(items);
} catch (error) {
res.status(500).send(error.message);
}
});

// Start the server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

Instructions to Run the App

Make sure you have Node.js installed.
Set up a new Node.js project using npm init.
Install the required packages by running npm install dotenv mongoose express.
Make sure you have a running MongoDB instance that the application can reach.
Create a .env file in your project root with the following:
PORT=3000
MONGODB_URI=your_mongodb_connection_string
Replace your_mongodb_connection_string with the actual connection string to your MongoDB instance.
Save the above JavaScript code as app.js in the root directory of your project.
Run the application with the command node app.js.
Your server should start, and you can access the API at localhost:3000 (or another port if you configure it differently).
This sets up a basic server using Express.js and Mongoose, capable of adding items and retrieving all items from the MongoDB database. As you develop further, you likely want to add additional CRUD operations and more complex route handling.HandlerFuncs.
User
will the express.js server run properly with this code?
Dr. MONGODB
Yes, the provided app.js code should run properly and allow the Express.js server to run, assuming that all the prerequisites are met:
You have MongoDB running and accessible at the URI provided in your .env file.
The dotenv, mongoose, and express packages are installed in your Node.js project.
The .env file is correctly configured with your MongoDB connection string and (optionally) your desired port number.
Node.js is installed on your machine.
This script sets up an Express.js server with Mongoose to connect to a MongoDB database. It includes error handling for the database connection and defines a Mongoose schema and model for warehouse items. The provided routes allow for adding new items with a POST request to /items and fetching all items with a GET request to the same endpoint.
Remember to handle potential environmental differences between development, testing, and production. Also, to keep your application secure, be sure not to commit the .env file to a public repository as it contains sensitive information such as the database connection string.
If you encounter any issues running the server, the error logs should provide more context, allowing you to debug and resolve any problems. If, after checking these details, the server doesn't run as expected, you may need to troubleshoot further or provide error messages for more specific assistance.
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.