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
Initialize npm. You can change the options or you can accept the default options.
npm init -y
Create an Express Server
Install Express and Mongoose
npm i express mongoose
Create server.js and open VS Code
touch server.js
code .
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 }`)
}
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
const express= require('express')const app = express()
const db = require('./db')
...
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)
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}
}
}
Remember to import the helper function in ./server.js
const { create } = require('./dbHelpers')
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’
})
})
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}
}
}
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
})
})
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}
}
}
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
})
})
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}
}
}
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
})
})
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}
}
}
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'
})
})
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 }
}
}
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'
})
})
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 :)