Mongoose is one of the fundamental tools for manipulating data for a Node.js and MongoDB backend.
In this article, you will be looking into using Mongoose with the MongoDB Atlas remote database. The example in this tutorial will consist of a list of food and their caloric values. Users will be able to create new items, read items, update items, and delete items.
async
and await
.Downloading and installing a tool like Postman is recommended for testing API endpoints.
This tutorial was verified with Node v15.3.0, npm
v7.4.0, express
v4.17.1, mongoose
v5.11.12, and MongoDB v4.2.
This project also requires a MongoDB Atlas account.
After creating an account and signing in, follow these steps to deploy a free tier cluster.
Once you have set a cluster, a database user, and an IP address you will be prepared for later acquiring the connection string as you set up the rest of your project.
In this section, you will create a directory for your project and install dependencies.
Create a new directory for your project:
- mkdir mongoose-mongodb-atlas-example
Navigate to the newly created directory:
- cd mongoose-mongodb-atlas-example
At this point, you can initialize a new npm project:
- npm init -y
Next, install express
and mongoose
:
- npm install express@4.17.1 mongoose@5.11.12
At this point, you will have a new project with express
and mongoose
.
In this section, you will create a new file to run the Express server, connect to the MongoDB Atlas database, and import future routes.
Create a new server.js
file and add the following lines of code:
const express = require("express");
const mongoose = require("mongoose");
const foodRouter = require("./routes/foodRoutes.js");
const app = express();
app.use(express.json());
mongoose.connect(
"mongodb+srv://madmin:<password>@clustername.mongodb.net/<dbname>?retryWrites=true&w=majority",
{
useNewUrlParser: true,
useFindAndModify: false,
useUnifiedTopology: true
}
);
app.use(foodRouter);
app.listen(3000, () => {
console.log("Server is running...");
});
Pay attention to the connection string. This is the connection string that is provided by MongoDB Atlas. You will need to replace the administrator account (madmin
), password, cluster name (clustername
), and database name (dbname
) with the values that are relavent to your cluster:
mongodb+srv://madmin:<password>@clustername.mongodb.net/<dbname>?retryWrites=true&w=majority
mongoose.connect()
will take the connection string and an object of configuration options. For the purpose of this tutorial, the useNewUrlParser
, useFindAndModify
, and useUnifiedTopology
configuration settings are necessary to avoid a deprecation warning.
At this point, you have the start of an Express server. You will next need to define the schema and handle routes.
First, you will need to have a pattern to structure your data on to and these patterns are referred to as schemas. Schemas allow you to decide exactly what data you want and what options you want the data to have as an object.
In this tutorial, you will use the mongoose.model
method to make it usable with actual data and export it as a variable you can use in foodRoutes.js
.
Create a new models
directory:
- mkdir models
Inside of this new directory, create a new food.js
file and add the following lines of code:
const mongoose = require("mongoose");
const FoodSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
lowercase: true,
},
calories: {
type: Number,
default: 0,
validate(value) {
if (value < 0) throw new Error("Negative calories aren't real.");
},
},
});
const Food = mongoose.model("Food", FoodSchema);
module.exports = Food;
This code defines your FoodSchema
. It will consist of a name
value that is of type String
, it will be required
, trim
any whitespace, and set to lowercase
characters. It will also consist of a calories
value that is of type Number
, it will have a default
of 0, and validate
to ensure no negative numbers are submitted.
Once you have your data model set up, you can start setting up routes to use it. This will utilize various querying functions available through Mongoose.
You will start by reading all foods in the database. At this point, it will be an empty array.
Create a new routes
directory:
- mkdir routes
Inside of this new directory, create a new foodRoutes.js
file and add the following lines of code:
const express = require("express");
const foodModel = require("../models/food");
const app = express();
app.get("/foods", async (request, response) => {
const foods = await foodModel.find({});
try {
response.send(foods);
} catch (error) {
response.status(500).send(error);
}
});
module.exports = app;
This code establishes a /foods
endpoint for GET requests (note the plural ‘s’). The Mongoose query function find()
returns all objects with matching parameters. Since no parameters have been provided, it will return all of the items in the database.
Since Mongoose functions are asynchronous, you will be using async/await
. Once you have the data this code uses a try/catch
block to send it. This will be useful to verify the data with Postman.
Navigate to the root of your project directory and run your Express server with the following command in your terminal:
- node server.js
In Postman, create a new Read All Food request. Ensure the request type is set to GET
. Set the request URL to localhost:3000/foods
. And click Send.
Note: If you need assistance navigating the Postman interface for requests, consult the official documentation.
The Postman results will display an empty array.
Next, you will build the functionality to create a new food item and save it to the database.
Revisit the foodRoutes.js
file and add the following lines of code between app.get
and module.exports
:
// ...
app.post("/food", async (request, response) => {
const food = new foodModel(request.body);
try {
await food.save();
response.send(food);
} catch (error) {
response.status(500).send(error);
}
});
// ...
This code establishes a /food
endpoint for POST requests. The Mongoose query function .save()
is used to save data passed to it to the database.
In Postman, create a new request called Create New Food. Ensure the request type is set to POST
. Set the request URL to localhost:3000/food
.
In the Body section, select raw and JSON. Then, add a new food item by constructing a JSON object with a name
and calories
:
{
"name": "cotton candy",
"calories": 100
}
After sending the Create New Food request, send the Read All Food request again. The Postman results will display the newly added object.
Every object created with Mongoose is given its own _id
and you can use this to target specific items. It will be a mix of alphabetical characters and letters. For example: 5d1f6c3e4b0b88fb1d257237
.
Next, you will build the functionality to update an existing food item and save the changes to the database.
Revisit the foodRoutes.js
file and add the following lines of code between app.post
and module.exports
:
// ...
app.patch("/food/:id", async (request, response) => {
try {
await foodModel.findByIdAndUpdate(request.params.id, request.body);
await foodModel.save();
response.send(food);
} catch (error) {
response.status(500).send(error);
}
});
// ...
This code establishes a /food/:id
endpoint for PATCH requests. The Mongoose query function .findByIdAndUpdate()
takes the target’s id
and the request data you want to replace it with. Then, .save()
is used to save the changes.
In Postman, create a new request called Update Food. Ensure the request type is set to PATCH
. Set the request URL to localhost:3000/food/<id>
, where id
is the identifying string of the food you created previously.
In the Body section, select raw and JSON. Then, modify your food item by constructing a JSON object with a name
and calories
:
{
"calories": "999"
}
After sending the Update Food request, send the Read All Food request again. The Postman results will display the object with modified calories
.
Finally, you will build the functionality to remove an existing food item and save the changes to the database.
Revisit the foodRoutes.js
file and add the following lines of code between app.patch
and module.exports
:
// ...
app.delete("/food/:id", async (request, response) => {
try {
const food = await foodModel.findByIdAndDelete(request.params.id);
if (!food) response.status(404).send("No item found");
response.status(200).send();
} catch (error) {
response.status(500).send(error);
}
});
// ...
This code establishes a /food/:id
endpoint for DELETE requests. The Mongoose query function .findByIdAndDelete()
takes the target’s id
and removes it.
In Postman, create a new request called Delete Food. Ensure the request type is set to DELETE
. Set the request URL to localhost:3000/food/<id>
, where id
is the identifying string of the food you created previously.
After sending the Delete Food request, send the Read All Food request again. The Postman results will display an array without the item that was deleted.
Note: Now that you have completed this tutorial, you may wish to Terminate any MongoDB Atlas clusters that you are no longer using.
At this point, you have an Express server using Mongoose methods to interact with a MongoDB Atlas cluster.
In this article, you learned how to use Mongoose methods you can quickly make and manage your backend data.
If you’d like to learn more about Node.js, check out our Node.js topic page for exercises and programming projects.
If you’d like to learn more about MongoDB, check out our MongoDB topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
On Step 6, why do you have to run model.save()? Isn’t findByIdAndUpate() supposed to update by itself the document (if found)?
For deletion, the findByIdAndDelete() function already performs the deletion, so why did the findByIdAndUpdate() function have a following save()?
The save() call in the document creation makes sense, since you’re instantiating a new model (new doc) and then saving it, but it doesn’t make sense to me in the update.
Thanks!
Gooday sir Do you have a git repo for this. I wud much appreciate if you have it hosted too Or u want me to figure it out too
The challenge I been having with these tutorials is transitioning from ‘localhost’ to remote host. That’s why I asked you the above questions
Great tutorial!
Think I may have found one small error: In “File Setup” you refer to the file ./models/FoodModel.js but later refer to the same file as ./models/food.js. Perhaps, I missed something obvious? Thanks for the very helpful tutorial.