#100DaysOfMERN - Day 27

#100DaysOfMERN - Day 27

·

13 min read

✏ #100DaysOfMERN - the App: Setup

As I've never built something like that before, I'm a bit clueless about the steps required, but I should probably start with creating a project folder called mern, and npm-init it to create a package.json:

npm init

Then, as usual, first make sure that MongoDB is running in the background by cd-ing into the mongo bin folder and typing

mongo

I've learned some basic operations yesterday with the Mongo Shell, so I'm going to create my database and the first collection from the command line. I've decided to call the database merndb (was originally postsdb, but this is more general), and the (probably only) collection inside will be collectionPosts:

use merndb
db.createCollection('collectionPosts')

All set!

Some considerations in advance

It won't make much sense to plan everything out in detail before I've even learned how to manage a database, but from what I already know, I can at least roughly tell what my data records will look like - here's an example for Day 1:

{
    title: '#100 Days of MERN - Day 1',
    tags: ['mern', 'node', 'command line'],
    summary: ['What is the MERN stack', 'How to install Node.js'],
    published:new Date(2020, 11, 29).toLocaleDateString("en-GB"),
    slug: '/100daysofmern-day-1',
    date: Date()
}
  • title: will really just be counting up the days
  • tags: self-explanatory
  • summary: an array with some questions I've covered in the post (not really a summary, but it provides more information than just a list of tags)
  • published: publication date
  • slug: I'll certainly need the links to each post later, if my app is supposed to lead me to the post I'm looking for
  • date: the date when the entry was created in the db

There's some redundancy in there. Both the title and the url follow a pattern, so I could generate them instead of hardcoding them. I could even leave out both, and - knowing that my database will have 100 entries at the end, in the order of when they were published - extract both the title and the url/slug from an array index.

I don't think it hurts though to not over-optimise for such a small project.

Of course I could add a lot more data like the word count, read time, or even likes (yikes), but this is just to get started. Also, I want to see how hard/easy it is to make such additions later.

Creating the first entry

Just for fun, I'll add that first entry now via the command line (have mongo running and make sure you're in the right database):

db.posts.insertOne({
    title: '#100 Days of MERN - Day 1',
    tags: ['mern', 'node', 'command line'],
    summary: ['What is the MERN stack', 'How to install Node.js'],
    published:new Date(2020, 11, 29).toLocaleDateString("en-GB"),
    slug: '/100daysofmern-day-1',
    date: Date()
})

Command line gives me a response that the insert was successful, but nevertheless I want to check out myself (of course I could also use Compass):

db.collectionPosts.find().pretty()

There it is ✌

I already regret that I gave my collection such an unnecessarily long name. I'm going to change that to just posts, before it gets on my nerves:

db.collectionPosts.renameCollection('posts')

So, with these few commands I already have everything I need to build my whole database. I could manually add a new post object to the collection after each blog - simple. But that's not particularly advanced (and prone to errors), and the main purpose is not just to build a database, but rather to query it, once it's been built.

I had originally planned to jump right into Mongoose now, however, it would be interesting to check out how it works without Mongoose. But first, some more fundamentals:


✏ From Mongo's core to my JavaScript code

How exactly does Mongo store data? A word that comes to mind immediately is "JSON objects", but that's not what's actually being saved by Mongo.

JSON (= JavaScript Object Notation) is an awesome format, you can have completely unstructured data, easily accessible through keys. As it's summed up in tons of articles: "It's a lightweight, text-based, language-independent interchange format." In the words of its inventor: It's the Fat-Free Alternative to XML.

With my limited knowledge in other programming languages, I interpret the above as such: Every JSON Object can be turned into a string, and every language can process strings. I imagine it wouldn't be difficult to write a program in PHP that takes this string, and transforms it back into an associative array (something that PHP can handle).

JSON vs BSON

But as mentioned, JSON is not what's stored internally in Mongo. JSON (in its unminimised form) is perfectly human-readable, but, even when minimised, takes up a lot of unnecessary space.

That's where BSON helps - BSON means "Binary JSON". As we all know, binary is ultimately not-human-readable, and perfectly machine-readable. It's a computer's mother tongue, so storing and processing data in binary format is 100% optimised for these purposes.

In order for humans to interact with the database in a meaningful way, we need an interface that speaks both BSON and JSON. The MongoDB driver npm package is such an interface, it also comes with methods so we can connect to the database, and perform CRUD operatons on it.

Now let's finally see some code:


✏ Connecting to MongoDB without Mongoose

I'll create a new file withoutMongoose.js, just for testing this out. Also, I'll have to install the mongodb package and import it. Note that I'll specify the version, too, because the latest release seems to have a bug.

npm install mongodb@3.6.3

withoutMongoose.js

// following the documentation here
const MongoClient = require('mongodb').MongoClient;

To use the .connect method, I'll need

  • a connection string (in my case, mongodb://localhost/merndb)
  • a connection configuration object, which only takes care of avoiding some deprecation warnings (read more here: MongoDB deprecation warnings)

Adding both:

const mongoClient = require('mongodb').MongoClient;

const connStr = 'mongodb://localhost/merndb';

const connOptions = {
  useNewUrlParser: true,
  useUnifiedTopology: true
};

mongoClient.connect(connStr, connOptions, function(err, client){
    const db = client.db();
    console.log('mongo connected');
})

To see if I can insert something into the collection, I'll create a test object newPost with just a title, and use the already familiar method insertOne:

mongoClient.connect(connStr, connOptions, function(err, client){

    const db = client.db();
    console.log('mongo connected');

    const newPost = {title: 'Day 27'};
    db.collection('posts').insertOne(newPost, function(err, result){

        console.log(result.ops)

    })
})

Here, result contains the result document from MongoDB, and ops contains the documents inserted with added _id fields. I'm just logging it, but checking my database now shows that I've successfully added a new record 😃

In fact I've added a whole bunch of them, because while testing, I had nodemon running, which reran the script each time I made a change, and therefore my insert ran many times.

But that's not how you'd do it anyway. Usually, you'd have a server that can process a POST request, and only insert some new data within that request. Anyway, successful proof of concept.


✏ Resources

MongoDB without Mongoose


✏ Recap

I've learned

  • some fundamentals about how MongoDB internally handles data (BSON vs JSON)
  • how to connect to my database in my Node script with the MongoDB driver
  • how to insert a new data object

✏ Next:

  • setting up a simple server so I can test retrieving and creating data with GET/POST requests

✏ Thanks for reading!

I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subsribe to my newsletter. Until next time 👋


✏ Previous Posts

  • Day 1: Introduction, Node.js, Node.js in the terminal
  • Day 2: npm, node_modules, package.json and package-lock.json, local vs global installation of packages
  • Day 3: Create a React app without create-react-app, Webpack, Babel
  • Day 4: npx and cowsay
  • Day 5: npm vs. npx, npm audit, semantic versioning and update rules
  • Day 6: Call stack, event loop, JavaScript engine, JavaScript runtime
  • Day 7: Call stack and event loop in Node.js, setImmediate()
  • Day 8: setImmediate(), process.nextTick(), event loop phases in Node.js
  • Day 9: Network requests with XMLHttpRequest and callbacks
  • Day 10: Promises
  • Day 11: Network requests with XMLHttpRequest and Promises
  • Day 12: React Quiz App part 1
  • Day 13: React Hangman
  • Day 14: FullStackOpen course 1: details of GET and POST requests, request headers
  • Day 15: React Hangman: Trigger fetch with click event callback vs useEffect
  • Day 16: REST API and CRUD
  • Day 17: Boring Book App part 1: React Frontend, Express Backend, GET requests, CORS
  • Day 18: Boring Book App part 2: POST request, File System API
  • Day 19: Boring Book App part 3: Request Parameters, DELETE request
  • Day 20: Boring Book App part 4: PUT request
  • Day 21: Express JS vs Vanilla JS part 1: Server setup, routes
  • Day 22: Express JS vs Vanilla JS part 2: Serve static files with Vanilla Server
  • Day 23: Express JS vs Vanilla JS part 3: Serve static files with Express Server, Middleware
  • Day 24: Express JS: express.Router, Postman
  • Day 25: Express JS: express-handlebars
  • Day 26: MongoDB: Installation, noSQL database structure, Mongo Shell commands