Crud Express Mongodb
Crud Express Mongodb
Crud Express Mongodb
a Simple
CRUD app with
Express and
MongoDB
I finally understood how to work with Node, Express, and MongoDB. I want
to write a comprehensive tutorial so you won’t have to go through the
same headache I went through.
MongoDB is a database . This is the place where you store information for
your websites (or applications).
If we put CRUD, Express and MongoDB together into a single diagram, this
is what it would look like:
Does CRUD, Express and MongoDB makes more sense to you now?
Free free to check out the demo before continuing with this tutorial.
This article is LONG! Remember to grab the source code by leaving your
name and email address in this form. I’ll also send you this article in PDF so
you can read it at your leisure.
By the way, I’m not going to focus on the styles since we’re focusing on
learning Crud, Express, and MongoDB in this tutorial.
Prerequisites
You’ll need two things to get started with this tutorial:
1. You are not afraid of typing commands into a Command Line. If
you’re afraid, use this article to get over your fear .
2. You need to have Node installed.
To check if you have Node installed, open up your Command Line and run
the following code:
$ node -v
You should get a version number if you have Node installed. If you don’t,
you can install Node either by downloading the installer from Node’s
website or downloading it through package managers like Homebrew
(Mac) and Chocolatey (Windows).
Getting started
Start by creating a folder for this project. Feel free to call it anything you
want. After you’ve created the folder, navigate into it with the Terminal and
run npm init .
$ npm init
Just hit enter through everything that appears. I’ll talk about the ones you
need to know as we go along.
touch server.js
Next, put this a console.log statement into server.js . This lets us
know whether Node is running properly.
// server.js
console.log('May Node be with you')
Now, run node server.js in your command line and you should see
this:
Using Express
First, we have to install Express. We can do this by running the
command. (
npm install npm is installed with Node, which is why
you use commands like npm init and npm install ).
app.listen(3000, function() {
console.log('listening on 3000')
})
Now, run node server.js and navigate to localhost:3000 on your
cannot get
browser. You should see a message that says / .
CRUD - READ
Browsers perform the READ operation when you visit a website. Under the
hood, they send a GET request to the server to perform this READ
operation.
cannot get
You see / because our server sent nothing back to the
browser.
app.get(endpoint, callback)
endpoint is the requested endpoint. It’s the value that comes after your
domain name. Here are some examples:
I’m going to start writing in ES6 code and show you how to convert to ES6
along the way as well. First off, I’m replacing function() with an ES6
arrow function. The below code is the same as the above code:
CTRL +
1. Stop the current server by hitting C in the command line.
2. Run node server.js again.
Great.
touch index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MY APP</title>
</head>
<body>
<h1> May Node and Express be with you. </h1>
</body>
</html>
Restart your server and refresh your browser. You should be able to see
your HTML file now.
At this point, you probably have realized that you need to restart your
server whenever you make a change to server.js . This is process is
incredibly tedious, so let’s take a quick detour and streamline it by using a
tool called nodemon.
Enter Nodemon
Nodemon restarts the server automatically when you save a file that’s used
by the server.js . We can install Nodemon with the following
command:
package.json file.
Nodemod behaves like Node. So you can run nodemon server.js and
you’d expect to see the same thing. Unfortunately, this only works if you’ve
installed nodemon globally with the -g flag (and we didn’t do this).
We have other ways to run Nodemon. For example, you can execute
Nodemon directly from the node_modules folder. This is super unweildy,
but it works:
./node_modules/.bin/nodemon server.js
We can make things simpler by adding script key in the
package.json file. This lets us run nodemon server.js without the
./node_modules... preamble.
{
// ...
"scripts": {
"dev": "nodemon server.js"
}
// ...
}
npm run
Now, you can run dev to trigger nodemon server.js .
Back to the main topic. We’re going to cover the CREATE operation next.
CRUD - CREATE
Browsers can only perform a CREATE operation if they send POST request
to the server. This POST request can be triggered through JavaScript or
through a <form> element.
Let’s figure out how to use a <form> element to create new entries for
this Star Wars quote application for now. We’ll examine how to send
requests via JavaScript later.
1. An action attribute
2. A method attribute
3. name attributes on each <input> elements within the form
The method tells browsers what kind of request to send. In this case, we
use POST because we’re sending a POST request.
The action attribute tells the browser where to send the POST
request. In this case, we’re send the POST request to /quotes .
Great, we know that Express is handling the form for us right now. The next
question is, how do we get the input values with Express?
Turns out, Express doesn’t handle reading data from the <form>
element on it’s own. We have to add another package called body-parser
to gain this functionality.
You should be able to see values from the <form> element inside
req.body now. Try doing a console.log and see what it is!
app.post('/quotes', (req, res) => {
console.log(req.body)
})
Hmmm.
Master Yoda has spoken! Let’s make sure we remember Yoda’s words. It’s
important. We want to be able to retrieve it the next time we load our
index page.
MongoDB
MongoDB is a database. We can store information into this database to
remember Yoda’s words. Then, we can retrieve this information and display
to people who view our app.
The next part is to get the correct link to our database. Most people store
their databases on cloud services like MongoDB Atlas. We’re going to do
same as well. (It’s free).
You can also create a database on your computer for development work.
Read “How to setup a local MongoDB Connection ” for instructions.
You also need to select a cloud service. Go ahead with MongoDB Atlas in
this case.
Next, you need to set permissions for users. MongoDB Atlas will
automatically fill up your current email address as the user. So just continue
to the next step.
Next, you need to create a Database in MongoDB Atlas. There are several
steps to do this.
First, you need to create a new Project. You can do this by going under
“Context” in the top left hand menu. Click the Dropdown. Then, select New
Project.
Next, you will need to name your project. Call it anything you want. I’m
going to call this star-wars .
Then, you will need to add members. Again, you’re already added so go
ahead and click “Create Project” to move on.
Click on Create cluster next. You should see “Your cluster is being created”.
You have to wait for approximately 5 minutes for the cluster creation.
When the cluster is ready, you’ll see this:
Now, we need to connect our Star Wars app with this cluster.
You need to whitelist your IP address before you can connect to your
cluster. This is a security feature built into MongoDB Atlas. Go ahead and
click “Add your Current IP Address”.
Next, you need to create a MongoDB user. This username and password is
different from the one you used to login to MongoDB Atlas. This username
and password is used ONLY for the database.
Make sure you remember MongoDB user and password. We’ll use it to
connect to the database.
'mongodb+srv://<username>:<password>@<clustername>-
rmp3c.mongodb.net/test?retryWrites=true&w=majority'
You can remove the deprecation warning by adding the option into
MongoClient.connect
MongoClient.connect(connectionString, {
useUnifiedTopology: true
}, (err, client) => {
if (err) return console.error(err)
console.log('Connected to Database')
})
Like Databases, you can name collections anything you want. In this case,
let’s store quotes into a quotes collection. We use db.collection
// ...
})
We can use the insertOne method to add items into a MongoDB
collection.
Try submitting the <form> from the browser. You should see a big scary
looking result in the Terminal.
If you see this, congratulations! You’ve successfully add the quote into the
database.
You can check the items inside the database by going to “Collections” in
MongoDB Atlas.
You should see a document in your database. (Each database entry is called
a document).
If you go back to the Browser, you’ll see it’s still trying to load something.
This happens because the browser expects something back from the
server.
In this case, we don’t need to send the browser information. Let’s ask the
browser to redirect back to / instead. We do this with
res.redirect .
Yay!
Since we have some quotes in the collection, let’s show them to our user
when they land on the page!
The find method returns a cursor which won’t make sense if you
tried logging it.
But this cursor object contains all quotes from our database! It has a
bunch of method that lets us get our data. For example, we can use
toArray to convert the data into an array.
Great! We see the quotes we added! (You see so many of the same quotes
because I added them all when writing this tutorial ��).
I’ve wrote extensively about the how and why of template engines in a
separate post. You might want to check it out if you have no idea what
template engines are.
I use Nunjucks as my template engine of choice. Feel free to check out the
post to find out why.
For this tutorial, we will use Embedded JavaScript (EJS) as our template
engine because it’s the easiest to start with. You’ll find it familiar from the
get-go since you’ll be writing HTML and JavaScript.
Using EJS
First, we need to install EJS.
view
Next, we need to set engine to ejs . This tells Express we’re
using EJS as the template engine. You can need to place it before any
app.use , app.get or app.post methods.
We can now generate HTML that contains the quotes . This process is
called rendering the HTML.
res.render(view, locals)
view is the name of the file we’re rendering. This file must be
placed inside a views folder.
locals is the data passed into the file.
Let’s create a view. We’ll make an index.ejs file inside the views folder.
mkdir views
touch views/index.ejs
<body>
<h1>May Node and Express be with you.</h1>
If you refresh the page, you should still see the same thing. Nothing should
change, nothing should break.
Let’s put the quotes into index.ejs . To do this, we need to pass the
quotes into the render method.
We need to loop through the quotes. We can do this with a for loop.
In EJS, we write a for loop like how we write a JavaScript for loop. The
only difference is we need to put the for loop statements between
<% and %> .
<ul class="quotes">
<!-- Loop through quotes -->
<% for(var i = 0; i < quotes.length; i++) {%>
<li class="quote">
<!-- Output name from the iterated quote object -->
<span><%= quotes[i].name %></span>:
<!-- Output quote from the iterated quote object -->
<span><%= quotes[i].quote %></span>
</li>
<% } %>
</ul>
CRUD - UPDATE
We use the UPDATE operation when we want to change something. It can
be triggered with a PUT request. Like POST , PUT can be triggered
either through JavaScript or through a <form> element.
Let’s switch things up and use JavaScript since you already know how to
use <form> elements.
For this update operation, we will create a button that replaces the first
quote by Yoda to something written by Darth Vadar.
<div>
<h2>Darth Vadar invades!</h2>
<p>
Replace first Yoda's quote with a quote written by Darth
Vadar
</p>
<button id="update-button">Replace Yoda's quote</button>
</div>
$ mkdir public
$ touch public/main.js
app.use(express.static('public'))
<body>
<!-- ... -->
<script src="/main.js"></script>
</body>
We will send a PUT request when the button gets clicked. This means
we need to listen to a click event.
Next, we’re going to send the PUT request when the button is clicked:
// main.js
const update = document.querySelector('#update-button')
update.addEventListener('click', _ => {
// Send PUT Request here
})
fetch(endpoint, options)
In this case, let’s say we want to send the request to /quotes . We’ll set
endpoint to /quotes .
update.addEventListener('click', _ => {
fetch('/quotes', {/* ... */})
})
update.addEventListener('click', _ => {
fetch('/quotes', {
method: 'put'
})
})
Modern applications send JSON data to servers. They also receive JSON
data back to servers. JSON stands for JavaScript Object Notation. They’re
like JavaScript objects, but each property and value are written between
two quotation marks.
const data = {
name: 'Darth Vadar',
quote: 'I find your lack of faith disturbing.'
}
And what its JSON counterpart looks like. (Notice how everything is
wrapped between two " ).
{
"name": "Darth Vadar",
"quote": "I find your lack of faith disturbing."
}
We need to tell the server we’re sending JSON data by setting the
Content-Type headers to application/json .
update.addEventListener('click', _ => {
fetch('/quotes', {
method: 'put',
headers: { 'Content-Type': 'application/json' },
})
})
Next, we need to convert the data we send into JSON. We can do this with
JSON.stringify . This data is passed via the body property.
update.addEventListener('click', _ => {
fetch('/quotes', {
method: 'put',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Darth Vadar',
quote: 'I find your lack of faith disturbing.'
})
})
})
app.use(bodyParser.json())
Next, we can handle the PUT request with a put method. You
should be able to see the values we send from the fetch request.
The next step is to change the Yoda’s first quote to this quote by Darth
Vadar.
quotesCollection.findOneAndUpdate(
query,
update,
options
)
.then(result => {/* ... */})
.catch(error => console.error(error))
query lets us filter the collection with key-value pairs. If we want to filter
{ name: 'Yoda'
quotes to those written by Yoda, we can set } as the
query.
quotesCollection.findOneAndUpdate(
{ name: 'Yoda' },
update,
options
)
.then(result => {/* ... */})
.catch(error => console.error(error))
We will use the $set operator since we’re changing Yoda’s quotes into
Darth Vadar’s quotes:
quotesCollection.findOneAndUpdate(
{ name: 'Yoda' },
{
$set: {
name: req.body.name,
quote: req.body.quote
}
},
options
)
.then(result => {/* ... */})
.catch(error => console.error(error))
In this case, it’s possible that no Yoda quotes exist in the database. We can
force MongoDB to create a new Darth Vadar quote if no Yoda quotes exist.
We do this by setting upsert to true . upsert means: Insert a
document if no documents can be updated.
quotesCollection.findOneAndUpdate(
{ name: 'Yoda' },
{
$set: {
name: req.body.name,
quote: req.body.quote
}
},
{
upsert: true
}
)
.then(result => {/* ... */})
.catch(error => console.error(error))
Try clicking the “replace first Yoda quote” button in the browser. You
should see this result in your command line. This says we changed one of
Yoda’s quote.
If you refresh the browser, you should see Darth Vadar’s quote as the first
quote.
Finally, we need to respond to the JavaScript that sent the PUT request.
In this case, we’ll simply send the success message.
Next, we can handle the response from the server via a then object.
(We do this because fetch returns a promise). However, Fetch is
slightly different from most promises. You need to use another then
object to get the response from the server.
fetch({ /* request */ })
.then(res => {
if (res.ok) return res.json()
})
.then(response => {
console.log(response)
})
You should be able to see a Success message from the server in the
console.
I wrote an article on the Fetch API if you’re wondering why we need two
then calls. Give it a read! It’ll help cement your understanding.
If you are working on a fancy webapp, you can use JavaScript to update the
DOM, so users see the new changes immediately.
However, updating the DOM is out of scope of this article, so we’re just
going to refresh the browser to see the changes.
fetch({ /* request */ })
.then(res => {
if (res.ok) return res.json()
})
.then(response => {
window.location.reload(true)
})
If you want to learn to use JavaScript to update the DOM, I suggest going
through my Learn JavaScript course. I even teach you how make your
interface fast and snappy! (Check the Todolist Component).
CRUD - DELETE
The DELETE operation can be triggered through a DELETE request. It’s
similar to the UPDATE request so this should be simple if you understand
what we’ve done above.
<div>
<h2>Remove Darth Vadar!</h2>
<p>
Delete one Darth Vadar's quote. Does nothing if there are no
more Darth
Vadar's quote
</p>
<button id="delete-button">Delete Darth Vadar's quote</button>
</div>
Then, we’ll trigger a DELETE request through Fetch when a user clicks the
delete button.
deleteButton.addEventListener('click', _ => {
fetch('/quotes', {
method: 'delete',
})
})
Since we’re deleting a quote by Darth Vadar, we only need to send Darth
Vadar’s name to the server.
deleteButton.addEventListener('click', _ => {
fetch(/* ... */, {
method: 'delete',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Darth Vadar'
})
})
.then(res => {
if (res.ok) return res.json()
})
.then(data => {
window.location.reload()
})
})
We can then handle the event on our server side with the delete
method:
quotesCollection.remove(
query,
options
)
.then(result => {/* ... */})
.catch(error => console.error(error))
quotesCollection.remove(
{ name: 'Darth Vadar' },
options
)
.then(result => {/* ... */})
.catch(error => console.error(error))
However, since we already pass the name Darth Vadar from Fetch, we
don’t need to hardcode it in Express anymore. We can simply use
req.body.name .
Then, we can send a response back to the JavaScript in the then call.
Now, when you click the delete button, the browser will sends DELETE
request through Fetch to our Express server. Then, the server responds by
sending either an error or a message back.
No quote to
If the JavaScript receives a delete response, we can tell
the user there’s no Darth Vadar quote to delete.
To do this, let’s add an element where we can tell users about this message.
<div id="message"></div>
No quote to
If we receive delete , we can change the textContent
deleteButton.addEventListener('click', _ => {
fetch(/* ... */)
.then(/* ... */)
.then(response => {
if (response === 'No quote to delete') {
messageDiv.textContent = 'No Darth Vadar quote to delete'
} else {
window.location.reload(true)
}
})
.catch(/* ... */)
})
You have now learned all you need to know about creating simple
applications with Node, Express, and MongoDB. Now, go forth and create
more applications, young padawan. May the force be with you.
Further reading
Here’s some further readings if you’re interested to continue with the
Node, Express, MongoDB journey
Express articles
3 useful Express middleware
Handling Express errors
JavaScript Async/await
Using Async/await in Express
MongoDB articles
Mongoose 101
Testing related articles
Endpoint testing with Jest and Supertest
Connecting Jest and Mongoose