Getting Started with Mongoose
Tags: mongoose, node, mongodb, backenddevelopment
Published: 2024-06-28 10:35:35

##PAGE CONTENT
* [Introduction to Mongoose](#introduction-to-mongoose)
* [Connecting to the database](#connecting-to-the-database)
* [Creating Models and Schemas.](#creating-models-and-schemas)
* [Using Your Model in Controller Functions.](#using-your-model-in-controller-functions)
* [Conclusion.](#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](mongoosejs.com) official documentation. 

![Happy coding](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyzjiyx8vf80beet7xvb.png)