How to Build a Simple CRUD API using NodeJS, Express, and MongoDB
Tags: node, mongodb, mongoose, express
Published: 2021-11-10 20:12:30

In this post, we will be building a CRUD API with nodeJS and Express.

## Introduction

CRUD stands for **Create**(Insert data to database), **Read** (Retrieve data from database), **Update** (Edit data from database), and **Delete** (delete data from database) 

It is a popular design through which Web APIs interact with databases.

### This tutorial shows you how to:
* Set up an express server
* Set up routes with express
* Connect to MongoDB using Mongoose
* Write mongoose models
* Use Mongoose to make database queries

### Prerequisites
* At least node 8.10 and npm >= 5.6
* Working knowledge of nodeJS
* JavaScript concepts like arrow functions, object destructuring, and async/await.

> I am using Ubuntu 18.04 and a VS Code editor

### Create a new folder
Open your terminal and create a new folder and move into it.

```bash
mkdir crud-api-nodejs && cd crud-api-nodejs
```

Initialize npm. You can change the options or you can accept the default options.
```bash
npm init -y
```

### Create an Express Server
Install Express and Mongoose
```bash
npm i express mongoose
```

Create server.js and open VS Code
```bash
touch server.js
code .
```

In `./server.js`, add the following code

```javascript
// Require express
const express = require('express')
// Initialize express
const app = express()

// parse json objects
app.use(express.json()) 

// parse url encoded objects- data sent through the url
app.use(urlencoded({ extended: true})) 

// create a server
const PORT = 8080
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT  }`)
}
```

**In the above code, we have,**

* Added express to our app
* Initialized express
* Added middleware to parse JSON and URL encoded data.
* Started a server

### Connect to database
In `./db.js`
```javascript
const mongoose = require('mongoose')
const dbURI = 'mongodb://localhost:27017/bookDb'
mongoose.connect(dbURI, {useNewUrlParser:true})
// connect to database
const db = mongoose.connection
// if error
db.on("error", (err) => {
  console.error(`err: ${err}`)
})// if connected
db.on('connected', (err, res) => {
  console.log('Connected to database')
})```

Import `./db.js` to `./server.js`
```javascript
   const express= require('express')const app = express()
   const db = require('./db')
   ...
```

Create Book Schema Model in ./bookSchema.js
```javascript
const mongoose = require('mongoose')const bookSchema = 
mongoose.Schema({
  title: {
  type: String,
  required: true
  },
  author: {
    type: String,
    required: true
  },
  publisher: {
    type: String,
    required: true
  },
  read: {
    type: Boolean,
    required: true
  }
})
// Export model
module.exports = mongoose.model('Book', 
  bookSchema)
```

### Create
Here is what happens
* A client will send book info to our server through the request body.
* Check whether the data was actually sent, if not, send a bad request response
* Create a new record in the database
* If successful, send a 201 created response back

Let's first create a helper function for creating a database record.

In `./dbHelpers.js` add the following:
```javascript
const Book = require('./bookSchema')
   exports.create = async (data) => {
    try{
    const newBook = new Book(data)
    const savedBook = newBook.save()
    if(!savedBook) throw new Error('Book could not be saved')
    return {error: null}
    } catch (error) {
      return {error: error.message}
    }
}
```

Remember to import the helper function in `./server.js` 
```javascript
    const { create } = require('./dbHelpers')
```

Now in `./server.js`, add the following,
```javascript
app.post('/create', async (req, res) ⇒ {
  //check if req.body is empty
  if (!Object.keys(req.body).length) {
    res.status(400).json({
    message: 'Request body cannot be empty'
  })
  }
  const {title, author, publisher, read} = (req.body)
  // create a record in db
  const book = await create({title, author, publisher, read})
  if (book.error) {
    res.status(500).json({
      message: book.error
    })
  }
  res.status(201).json({
    message: ‘New book record created’
  })
})
```

### READ
**Read all**

To read all book records in the database, create a query that matches all the documents.
 
In `./dbHelpers.js`, add the following
```javascript
exports.readAll = async () => {
  try{
    const books = await Book.find({})
    if (!books) throw new Error('Book not found')
    return {error: null, data: books}
  }catch(error) {
      return {error: error.message, data: null}
  }
}
```
Add route in `./server.js`
```javascript
. . .
const {create, readAll } = require('./dbHelpers')
. . .
app.get('/read-all', async (req, res) => {
  const books = await readAll()
  if (books.error) {
    res.status(500).json({
      message: error.message,
      books: books.data
    })
  }
  res.status(200).json({
      message: 'success',
      books: books.data
    }) 
})
```
**Read One**

To retrieve one record, use `findById` and pass the Id in the URL as a parameter.

Add a helper function in `./dbHelpers.js`
```javascript
exports.readOne = async (id) => {
  try{
    const book = await Book.findByIdAndUpdate(id)
    if(!book) throw new Error('Could not retrieve book')
    return {error: null, data:book}
   } catch (error) {
     return {error: error.message, data:null}
  }
}
```
Add route in `./server.js`
```javascript
. . .
const {create, readAll, readOne } = require('./dbHelpers')
. . .
app.get('/read-one/:bookID', async (req, res) ⇒ {
  const book = await readOne(req.params.bookID)
  if (book.error) {
    res.status(500).json({
      message: book.error,
      books: book.data
    })
  }
  res.status(200).json({
      message: 'success',
      book: book.data
    }) 
})
```

### UPDATE
Pass in the id of the document you want to update via the URL and the data to update via the request body.

In `./dbHelpers.js`
```javascript
exports.update = async (id, data) ⇒ {
  try{
    const updatedBook = await Book.findByIdAndUpdate(id, data,{new: true})
    if(!updatedBook) throw new Error('Failed to update book')
    return {error: null, data: updatedBook}
   } catch (error) {
     return {error: error.message, data: null}
  }
}
```

In `./server.js`
```javascript
. . .
const {create, readAll, readOne, update } = require('./dbHelpers')
. . .
app.put('/update/:bookID', async (req, res) => {
   if (!Object.keys(req.body).length) {
       res.status(400).json({
       message: 'Request body cannot be empty',
       book: null
     })
  }

  const book = await update(req.params.bookID, req.body)
  if (book.error) {
    res.status(500).json({
      message: book.error,
      book: book.data
    })
  }
  res.status(200).json({
      message: 'success',
      book: book.data
    }) 
})
```

### DELETE
**Delete One**

Pass the Id of the document to delete via the URL.
In `./dbHelpers.js`
```javascript
exports.deleteOne = async (id) => {
  try{
    const isDeleted = await Book.findByIdAndDelete(id)
    if (!isDeleted) throw new Error('Failed to delete book')
    return { error: null}
  }catch (error) {
  return { error: error.message}
  }
}
```

In `./server.js`
```javascript
. . .
const {create, readAll, readOne, update, deleteOne } = require('./dbHelpers')
. . .
app.delete('/delete/:bookID', async (req, res) => {
  const isDeleted = await deleteOne(req.params.bookID)
  if (isDeleted.error) {
    res.status(500).json({
      message: isDeleted.error,
    })
  }
  res.status(200).json({
      message: 'Deleted Successfully'
    }) 
})
```

**Delete All**
In `./dbHelpers.js`
```javascript
exports.deleteAll = async () => {
  try{
    const isDeleted = await Book.deleteMany({})
    if (!isDeleted) throw new Error('Failed to delete books')
    return {error: null}
  }catch (error) {
  return { error: error.message }
  }
}
```
In `./server.js`
```javascript
. . .
const {create, readAll, readOne, update, deleteOne, deleteAll } = require('./dbHelpers')
. . .
app.delete('/delete-all', async (req, res) ⇒ {
  const isDeleted = await deleteAll(req)
  if (isDeleted.error) {
    res.status(500).json({
      message: isDeleted.error,
    })
  }
  res.status(200).json({
      message: 'Deleted Successfully'
    }) 
})
```


And that's it a simple CRUD API using mongoose, express, and nodeJS. 

I learned a lot when writing this post and I hope you learn something too.

If there is anything in this post that is not clear please let me know in the comments.

Thanks for reading and Happy coding :)