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 }
});

megaphone

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:

error

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, () => {
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.