MongoDB and Node.js Web Application¶
By popular demand, I've created a tiny web application that puts all these pieces together. There's a video on the videos page.
If we combine Node.js responding to requests with MongoDB to store data, we can have a complete database-backed web application that uses an event-loop rather than threads. Our app will support three main routes: a route to list the current favorite things, a route to insert a new favorite thing, and a route to delete a particular thing. So this is most of our CRUD API, skipping only the ability to edit a favorite thing.
All database interactions will be done using Ajax.
Here's the code for the app.js
file. Notice the three routes:
/things/
returns a list (JSON) of all documents in the collection./append/
adds another thing to the collection/delete/:oid
removes a thing from the collection using its unique object ID (OID).
// Following the tutorial at https://zellwk.com/blog/crud-express-mongodb/
const myPort = 1942;
const dbName = 'scottdb';
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser')
const mongo = require('mongodb');
const assert = require('assert');
const url = "mongodb://localhost:27017/"+dbName;
const client = new mongo.MongoClient(url, { useUnifiedTopology: true} );
var db;
var app = express();
app.use(bodyParser.urlencoded({extended: true}));
// app.use(express.static('static_files'))
const page = fs.readFileSync('page.html','utf8');
// respond with the page a GET request is made to the homepage
app.get('/', function (req, res) {
res.send(page);
});
function findThings(db, callback) {
var col = db.collection('things');
col.find({}).toArray( function(err, docs) {
assert.equal(null, err);
console.log('Found the following documents:');
for( var i = 0; i < docs.length; i++ ) {
console.log(i+': ',docs[i].thing);
}
if(callback) callback(docs);
});
};
// refresh the list of things
app.get('/things/', function (req,res) {
findThings(db, function (docs) { res.send(docs); });
});
// append a new thing; minimal response
app.post('/append/', function (req,res) {
console.log(req.body);
var col = db.collection('things');
console.log('appending ',req.body.desc);
col.insertOne({thing: req.body.desc});
console.log('ok');
res.send('ok');
});
// delete a thing; minimal response
app.post('/delete/:oid', function (req,res) {
let oid = req.params.oid;
var col = db.collection('things');
console.log('deleting ',oid);
var obj = {_id: mongo.ObjectId(oid)};
console.log(obj);
col.deleteOne(obj, function (err) { console.log('after deleteOne',err)});
console.log('ok');
res.send('ok');
});
// Startup code. Load the page once; all updates done via Ajax
client.connect(function (err) {
assert.equal(null, err);
// set global database connection
db = client.db(dbName);
// test finding things
findThings(db);
let msg = `Example app listening on port http://0.0.0.0:${myPort}!`
app.listen(myPort, () => console.log(msg));
});
Plus, there's the /
route that just returns the page. Below in the
HTML page. Notice the (empty) list of things and the JavaScript Ajax
functions to get the list of things, append a thing to the collection,
and delete something using its unique object ID (OID). The last uses a
delegated handler.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<!-- for mobile-friendly pages -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name=author content="Scott D. Anderson">
<meta name=description content="">
<meta name=keywords content="">
<link rel="stylesheet" href="http://cs.wellesley.edu/~anderson/sda-style.css">
<title></title>
</head>
<body>
<h1>Example of Node+Express+MongoDB</h1>
<h2>Favorite Things</h2>
<ol id="things"></ol>
<h2>UI</h2>
<p id="msg"></p>
<ul>
<li> <button id="get">get list of favorite things</button> </li>
<li>
<form>
<label for="thing_desc">Description</label>
<textarea id="thing_desc"></textarea>
<button type="button" id="put">add</button>
</form></li>
</ul>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$("#get").click(function (evt) {
console.log('refreshing list');
$.get('/things/', function (docs) {
$("#things").empty();
g = docs;
console.log('got '+docs.length+' things');
docs.forEach(function (doc) {
$('<li>')
.attr('data-oid',doc._id)
.text(doc.thing)
.appendTo("#things"); });
});
});
function refreshNeeded() {
// tell the user refresh to see the append
$("#msg").text('refresh things to see the effect').show();
setTimeout(function() { $("#msg").fadeOut(); }, 2000);
}
$("#put").click(function (evt) {
$.post('/append/',{'desc': $("#thing_desc").val()})
$("form")[0].reset();
refreshNeeded();
});
$("#things").on('click','[data-oid]', function (evt) {
let oid=$(this).attr('data-oid');
$.post('/delete/'+oid);
refreshNeeded();
});
</script>
</body>
</html>
A total of less than 150 lines of code to do a moderately complete, though tiny, web application.
Notice that the UI is intentionally a little annoying. I force the user to refresh the list of things, rather than using JavaScript to update the page. This is for several reasons:
- concurrency: it would be unrealistic to just have the front-end update the page, because that assumes no one else is editing the database. But in real life, they might, which means that the back end has to respond with an updated list of documents.
- simplicity: the back-end routes don't have to return an updated list of documents and send them back, and the front end doesn't have to handle the response.
Nevertheless, this would be easy to add. I'll leave that as an exercise for the reader.