DEV Community

Cover image for How to Build a Simple CRUD API using NodeJS, Express, and MongoDB
Mary Gathoni
Mary Gathoni

Posted on

How to Build a Simple CRUD API using NodeJS, Express, and MongoDB

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.

mkdir crud-api-nodejs && cd crud-api-nodejs
Enter fullscreen mode Exit fullscreen mode

Initialize npm. You can change the options or you can accept the default options.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Create an Express Server

Install Express and Mongoose

npm i express mongoose
Enter fullscreen mode Exit fullscreen mode

Create server.js and open VS Code

touch server.js
code .
Enter fullscreen mode Exit fullscreen mode

In ./server.js, add the following code

// 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  }`)
}
Enter fullscreen mode Exit fullscreen mode

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

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')
   ...
Enter fullscreen mode Exit fullscreen mode

Create Book Schema Model in ./bookSchema.js

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)
Enter fullscreen mode Exit fullscreen mode

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:

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}
    }
}
Enter fullscreen mode Exit fullscreen mode

Remember to import the helper function in ./server.js

    const { create } = require('./dbHelpers')
Enter fullscreen mode Exit fullscreen mode

Now in ./server.js, add the following,

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
  })
})
Enter fullscreen mode Exit fullscreen mode

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

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}
  }
}
Enter fullscreen mode Exit fullscreen mode

Add route in ./server.js

. . .
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
    }) 
})
Enter fullscreen mode Exit fullscreen mode

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

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}
  }
}
Enter fullscreen mode Exit fullscreen mode

Add route in ./server.js

. . .
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
    }) 
})
Enter fullscreen mode Exit fullscreen mode

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

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}
  }
}
Enter fullscreen mode Exit fullscreen mode

In ./server.js

. . .
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
    }) 
})
Enter fullscreen mode Exit fullscreen mode

DELETE

Delete One

Pass the Id of the document to delete via the URL.
In ./dbHelpers.js

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}
  }
}
Enter fullscreen mode Exit fullscreen mode

In ./server.js

. . .
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'
    }) 
})
Enter fullscreen mode Exit fullscreen mode

Delete All
In ./dbHelpers.js

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 }
  }
}
Enter fullscreen mode Exit fullscreen mode

In ./server.js

. . .
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'
    }) 
})
Enter fullscreen mode Exit fullscreen mode

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 :)

Top comments (0)