DEV Community

Cover image for How to Build an API with Mongoose and Express.js
Francisco Mendes
Francisco Mendes

Posted on

How to Build an API with Mongoose and Express.js

Overview

One of the reasons I like using NoSQL databases so much is because I can just start prototyping my API and at the same time I can start inserting data into the database. And for that same reason I love using monk, because I don't need to define any kind of schema, I just pass the data I want to insert into the database and it's done.

However, this is not always the case, sometimes I don't have all the collections defined in my head, but I have an idea of what each of the entities would be like. In this case it would be better to use something that helps me ensure consistency between the documents in a collection. This is where Mongoose comes in.

Today's example

In today's example we will create an API using the Express.js framework, then we will create a model with which we will CRUD our application.

Let's code

First let's install the necessary dependencies:

npm install express mongoose
Enter fullscreen mode Exit fullscreen mode

Then let's create a simple API:

// @/main.js
const express = require("express");

const app = express();

app.use(express.json());

app.get("/", async (req, res) => {
  return res.json({ message: "Hello, World ✌️" });
});

const start = async () => {
  try {
    app.listen(3000, () => console.log("Server started on port 3000"));
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

start();
Enter fullscreen mode Exit fullscreen mode

Now we will import the mongoose and connect it to our database.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const app = express();

// ...

const start = async () => {
  try {
    await mongoose.connect(
      "mongodb://root:root@localhost:27017/mongoose?authSource=admin"
    );
    app.listen(3000, () => console.log("Server started on port 3000"));
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

start();
Enter fullscreen mode Exit fullscreen mode

Now we can start creating our model. This one will have four fields. Name, breed, age and whether our four-legged friend is a good boy.

// @/models.js
const mongoose = require("mongoose");

const DogSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  breed: {
    type: String,
    required: true,
  },
  age: {
    type: Number,
    required: true,
  },
  isGoodBoy: {
    type: Boolean,
    required: false,
    default: true,
  },
});

const Dog = mongoose.model("Dog", DogSchema);

module.exports = { Dog };
Enter fullscreen mode Exit fullscreen mode

Now we can go back to our main file and we can start creating the routes that we are missing. But first we need to import the model into our file, then we can create our first route to go to the model and get all the dogs.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

const app = express();

app.use(express.json());

app.get("/dogs", async (req, res) => {
  const allDogs = await Dog.find();
  return res.status(200).json(allDogs);
});

// ...
Enter fullscreen mode Exit fullscreen mode

Now we can create a route to fetch just one dog, for that we will pass the id of the document we want to fetch in the endpoint's parameters.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

const app = express();

app.use(express.json());

app.get("/dogs", async (req, res) => {
  const allDogs = await Dog.find();
  return res.status(200).json(allDogs);
});

app.get("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  const dog = await Dog.findById(id);
  return res.status(200).json(dog);
});

// ...
Enter fullscreen mode Exit fullscreen mode

Now that we can get documents from multiple dogs and from just one dog. We still need to create one in the database, for that we will use the HTTP POST verb and we will add the data from the request body to the database.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

const app = express();

app.use(express.json());

app.get("/dogs", async (req, res) => {
  const allDogs = await Dog.find();
  return res.status(200).json(allDogs);
});

app.get("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  const dog = await Dog.findById(id);
  return res.status(200).json(dog);
});

app.post("/dogs", async (req, res) => {
  const newDog = new Dog({ ...req.body });
  const insertedDog = await newDog.save();
  return res.status(201).json(insertedDog);
});

// ...
Enter fullscreen mode Exit fullscreen mode

Then we will proceed with the implementation of updating the data of a document. Similar to what we did when searching for just one dog, we will pass the id in the params and then we will get the data from the request body.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

// ...

app.post("/dogs", async (req, res) => {
  const newDog = new Dog({ ...req.body });
  const insertedDog = await newDog.save();
  return res.status(201).json(insertedDog);
});

app.put("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  await Dog.updateOne({ id }, req.body);
  const updatedDog = await Dog.findById(id);
  return res.status(200).json(updatedDog);
});

// ...
Enter fullscreen mode Exit fullscreen mode

Last but not least we will implement the removal of a document from the collection, for that we will pass the id in the parameters and we will proceed with its elimination.

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

// ...

app.post("/dogs", async (req, res) => {
  const newDog = new Dog({ ...req.body });
  const insertedDog = await newDog.save();
  return res.status(201).json(insertedDog);
});

app.put("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  await Dog.updateOne({ id }, req.body);
  const updatedDog = await Dog.findById(id);
  return res.status(200).json(updatedDog);
});

app.delete("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  const deletedDog = await Dog.findByIdAndDelete(id);
  return res.status(200).json(deletedDog);
});

// ...
Enter fullscreen mode Exit fullscreen mode

As you may have noticed at the endpoints for updating and removing documents from the collection, the data of the updated/deleted element is returned in the response body just so that they have some kind of feedback on the action taken.

The final output from the main.js module should look like this:

// @/main.js
const express = require("express");
const mongoose = require("mongoose");

const { Dog } = require("./models");

const app = express();

app.use(express.json());

app.get("/dogs", async (req, res) => {
  const allDogs = await Dog.find();
  return res.status(200).json(allDogs);
});

app.get("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  const dog = await Dog.findById(id);
  return res.status(200).json(dog);
});

app.post("/dogs", async (req, res) => {
  const newDog = new Dog({ ...req.body });
  const insertedDog = await newDog.save();
  return res.status(201).json(insertedDog);
});

app.put("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  await Dog.updateOne({ id }, req.body);
  const updatedDog = await Dog.findById(id);
  return res.status(200).json(updatedDog);
});

app.delete("/dogs/:id", async (req, res) => {
  const { id } = req.params;
  const deletedDog = await Dog.findByIdAndDelete(id);
  return res.status(200).json(deletedDog);
});

const start = async () => {
  try {
    await mongoose.connect(
      "mongodb://root:root@localhost:27017/mongoose?authSource=admin"
    );
    app.listen(3000, () => console.log("Server started on port 3000"));
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

start();
Enter fullscreen mode Exit fullscreen mode

Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻‍💻

Hope you have a great day! 👌

Top comments (3)

Collapse
 
khemendrabhardwaj profile image
Khemendra-Bhardwaj

I was struggling for too long, absolutely loved it. Thanks a lot

Collapse
 
mr_ermatov profile image
Bakhromjon Ermatov

back end in one post fantastic bro

Collapse
 
hasnatnayeem profile image
Abul Hasnat Nayeem • Edited

Thank you for the nicely explained article. Just a small correction for the updateOne method. You need to use _id for the filtering.

await Dog.updateOne({ _id: id }, req.body);
Enter fullscreen mode Exit fullscreen mode