Getting Started with Mongoose
john mbugua
Posted on June 28, 2024
PAGE CONTENT
- Introduction to Mongoose
- Connecting to the database
- Creating Models and Schemas.
- Using Your Model in Controller Functions.
- Conclusion.
Introduction to Mongoose
Mongoose is an ODM (Object Data Modeling) library designed to streamline the interaction between your MongoDB database and Node.js applications.It is designed to work with asynchronous environments and offers a powerful set of features that simplify data manipulation and validation.
Connecting to the database
Before diving into Mongoose, you need to establish a connection to your MongoDB database. Here’s a step-by-step guide to get you started:
1.Installing Mongoose
npm install mongoose
2. Importing Mongoose and creating a connection file
Create a file called connect.js and place the connection logic there. This function returns a promise, which allows you to handle the connection success or failure in your main file.:
//connect.js
const mongoose = require('mongoose')
const connectDB = (url) => {
return mongoose.connect(url)
}
module.exports = connectDB
3. Use the Connection in Your Main File:
In your main application file for example app.js, import and use the connection logic:
//app.js
require('dotenv').config();
const express = require('express');
const app = express();
// connectdb
const connectDB = require("./db/connect"); //Add the path to the connection logic file.
// Get the MongoDB connection string from environment variables
const MONGODB_STRING = process.env.MONGODB_STRING;
app.get("/", (req, res) => {
res.send("Hello Heroku app");
});
const port = process.env.PORT || 4040;
// Function to start the server
const start = async () => {
try {
// Connect to the database. Pass the MongoDB String as an arguement of the connectDB function.
await connectDB(MONGODB_STRING)
app.listen(port, () =>
console.log(`Connected to the database and listening on port ${port}...`)
);
} catch (error) {
console.log(error);
}
};
start();
Explanation
- Environment Variables: Use dotenv to load MONGODB_STRING and PORT.
- Database Connection: connectDB in db/connect.js in my case returns a promise for the database connection.
- Async/Await: Use async/await in app.js to ensure the server starts only after a successful database connection.
- Error Handling: Handle connection errors with a try/catch block.
Creating Models and Schemas
In Mongoose, models and schemas are fundamental for defining and interacting with data in MongoDB.
- Schema: Defines the structure of your data. It specifies the shape that each document (instance) will have in the database. Think of it as a blueprint.
- Model: Once you have a schema, you compile it into a model. A model is a JavaScript class that represents a collection in MongoDB. It is responsible for creating and reading documents from the database.
- Document: An instance of a model is called a document. Each document is a record in your MongoDB collection, based on the schema you defined.
Create a file called job.js and place to place your logic there.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const JobSchema = new Schema(
{
company: {
type: String,
required: [true, "Please the field can't be empty"],
},
position: {
type: String,
required: [true, "Please the field can't be empty"],
},
status: {
type: String,
required: [true, "Please the field can't be empty"],
enum: ["interview", "declined", "pending", "accepted"],
default: "pending",
},
},
{ timestamps: true }
);
const JobModel = mongoose.model("Job", JobSchema);
module.exports = JobModel;
Explanation
- Schema Definition: *JobSchema * defines fields company, position, status _ and their types. It includes validations (required, enum) and a default value _default.
Model Creation: *JobModel * is created using mongoose.model(), linking Job as the collection name in MongoDB.
Using Your Model in Controller Functions
In an Express application using Mongoose, it's common to separate your model definitions from your controller logic. This promotes modularity and improves code organization. So you can create a file and place your logic there for controller functions.
- Controller Functions: These functions getAllJobs, createJob, getJob, updateJob, deleteJob handle CRUD operations for jobs. They interact with the imported (JobModel) to perform database operations based on HTTP requests.
- You will export this controller function and map them to the routes.
const JobModel = require("../models/Job");
const { StatusCodes } = require("http-status-codes");
const { BadRequestError, NotFoundError } = require("../errors/bad-request");
const getAllJobs = async (req, res) => {
const jobs = await JobModel.find({ createdBy: req.user.userId });
res.status(StatusCodes.CREATED).json({ jobs });
};
const createJobs = async (req, res) => {
req.body.createdBy = req.user.userId;
const jobs = await JobModel.create(req.body);
res.status(StatusCodes.CREATED).json({ jobs });
};
const getJob = async (req, res) => {
const {
user: { userId },
params: { id: job_id },
} = req;
const job = await JobModel.findOne({ _id: job_id, createdBy: userId });
if (!job) {
throw new NotFoundError(`No job with id: ${job_id}`);
}
res.status(StatusCodes.OK).json({ job });
};
const updateJob = async (req, res) => {
const {
body: { company, position },
params: { id: job_id },
user: { userId },
} = req;
if (company === "" || position === "") {
throw new BadRequestError("company and position cannot be found");
}
const job = await JobModel.findByIdAndUpdate(
{ _id: job_id, createdBy: userId },
req.body,
{ new: true, runValidators: true }
);
res.status(StatusCodes.OK).json({ job });
};
const deleteJob = async (req, res) => {
const {
params: { id: job_id },
user: { userId },
} = req;
const job = await JobModel.findByIdAndDelete({
_id: job_id,
createdBy: userId,
});
if (!job) {
throw new NotFoundError(`No user with id: ${job_id}`);
}
res.status(StatusCodes.OK).json({ msg: "Job deleted successfully" });
};
module.exports = {
getAllJobs,
createJobs,
getJob,
updateJob,
deleteJob,
};
Import Controller Functions:
Import controller functions getAllJobs, createJob, getJob, updateJob, deleteJob from the path your controller function login is located.
- Mapping Routes: Each HTTP method GET, POST, PUT, DELETE is mapped to its corresponding controller function getAllJobs, createJob, getJob, updateJob, deleteJob.
const express = require('express');
const router = express.Router();
const auth = require("../middleware/authentication");
const {
getAllJobs,
getJob,
createJobs,
updateJob,
deleteJob,
} = require("../controllers/jobs");
router.use(auth);
router.route('/').get(getAllJobs).post(createJobs);
router.route('/:id').get(getJob).patch(updateJob).delete(deleteJob);
module.exports = router;
Importing your router in your main file.
In an Express application, routers are used to organize and handle different sets of routes. Here is how you integrate a router into your main application file.
Import the router in your main file (app.js)
const jobRoutes = require("./routes/jobs");
Applying Router Middleware in your main file (app.js)
//app.js
require('dotenv').config();
const express = require('express');
const app = express();
//imported router
const jobRoutes = require("./routes/jobs");
// connectdb
const connectDB = require("./db/connect"); //Add the path to the connection logic file.
// Get the MongoDB connection string from environment variables
const MONGODB_STRING = process.env.MONGODB_STRING;
app.get("/", (req, res) => {
res.send("Hello Heroku app");
});
const port = process.env.PORT || 4040;
// Function to start the server
const start = async () => {
try {
// Connect to the database. Pass the MongoDB String as an arguement of the connectDB function.
await connectDB(MONGODB_STRING)
app.listen(port, () =>
console.log(`Connected to the database and listening on port ${port}...`)
);
} catch (error) {
console.log(error);
}
};
start();
Conclusion
Integrating Mongoose in Node.js simplifies MongoDB interactions through schema based modeling, enhancing data structure and validation. Explore more in the mongoosejs.com official documentation.
Posted on June 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.