Coding Journal

Jordan Vidrine

Daily Coding Journal

An experienced business owner + musician with a demonstrated history of management in the Energy sector and an insatiable appetite for learning new and complex skills. Transitioning from an ownership role & seeking employment in Web Development as a Jr Full Stack Developer.

July 1st 2019

I have decided to reformat most if not all journal entries from today foward to act more like a note taking platform and a more detailed description of what I am learning. I will be updating throughout my day, rather than at the end of the day.

This morning I went through a couple more hours of the Node.js course, and dove into MongoDB and how to get mongodb up and running in a node application and how to perform CRUD operations. I also learned how to utilize Promises, rather than proving a callback to each method, which makes things a lot less convoluted and easier to understand.

For example purposes lets pretend all of the following code is nested inside of the following call to MongoClient.connect The calls to console in the Then and Catch statements are for debugging purposes only.

MongoClient.connect(connectionURL, {useNewUrlParser: true}, (error, client) => {
  if (error) {
    return console.log('Unable to connect to database!')
  }
  const db = client.db(databaseName)

Creating a record

//insertMany(docs[,options, callback])
//if no callback is provided, a promise is returned
db.collection('tasks').insertMany([{
    description: 'Get groceries',
    completed: false
  },{
    description: 'Finish homework',
    completed: false
  }
]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

Reading a record

To get information from your database the find method is used. The find method returns a ‘Cursor’ which is an object with multiple methods and information that you can parse or call methods on to retrieve the info you need. This is a good thing because it allowed the data you searched for to be accessed in more ways than one.

For this purpose, I want to return all data in the ‘tasks’ collection. So I will call .toArray on the .find method, which without a callback, also returns a promise.

//find(query[,options])
//providing an empty object will find ALL items in that collection
//providing something like {description: 'Get groceries'} would return that one
//specific piece of data
db.collection('tasks').find({}).toArray().then((data)=>{
  console.log(data)
}).catch((error)=>{
  console.log(error)
})

Updating a record

//Use updateMany or UpdateOne depending on your needs
//updateMany(filter,update[,options, callback])
//update argument can be any number of 'update operators'
//they and their syntax use can be found at https://docs.mongodb.com/manual/reference/operator/update/#id1

//this will update all tasks that are not completed to complete

db.collection('tasks').updateMany({completed: false},{
  $set: { completed: true }
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

Deleting a record

///deleteMany or deleteOne depending on your needs
//deleteMany(filter[,options, callback])

//this will delete all completed tasks
db.collection('tasks').deleteMany({completed:true}).then((result)=>{
  console.log(result)
}).catch((error) =>{
  console.log(error)
})

After going through these videos and working through my own experiments and examples, I feel like I have a good understanding of the basic CRUD operations that mongodb is capable of.

REST APIs and Mongoose

This section of the course will be dealing with creating an express based REST API utilizing our knowledge of data storage. It will allow a user to sign up for an account, add tasks, delete tasks, etc. Mongoose will be used to model the data to be sent and delivered to the client.

Mongoose connects to the MongoDB and can create Models for collections we want to store in the database.

The example on the mongoose website looks like this:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

//this creates rules for the 'Cat' collection, that each item will be called 'name' and a String must be passed to that.
const Cat = mongoose.model('Cat', {name: String});

//new instance of Cat is made and stored in kitty
const kitty = new Cat({name: 'Zildjian'});

//saves the kitty instance of Cat
kitty.save().then(() => console.log('meow'));

Model Creation

Here is what I created in regards to models for these exercises. You can create schema to customize how the data should be entered for each collection. Schema Documentation

const mongoose = require('mongoose')
//database name comes after the port - creates the database when run for the first time
mongoose.connect('mongodb://127.0.0.1:27017/task-manager-api', {
  useNewUrlParser: true,
  useCreateIndex: true
})

//Creating the User Model
const User = mongoose.model('User', {
  name: {
    type: String
  },
  age: {
    type: Number
  }
})

//Creating the Tasks Model
const Task = mongoose.model('Task', {
  description: String,
  completed: Boolean
})

//example of making a new "task" and then saving it to the DB
let getGroceries = new Task({description: 'Get Groceries', completed: false})

getGroceries.save().then(() =>{
  console.log(getGroceries)
}).catch((error)=>{
  console.log('Error:', error)
})

Data Validation & Sanitation

With validation, we enforce that the data conforms to certain rules. Ie. {age: Number, min: 21, required: true} Sanitation allows us to alter the data before saving it. Ie. removing spaces before and after a users name. Mongoose Validation Docs

Custom Validation

Mongoose also allows you to setup custom validators, that work beyond the included ones that come with the service. It’s also smart to use highly rated NPM libraries for validation like credit cards, phone numbers, emails, etc.

age: {
  type: Number,
  validate(value) {
    if (value < 0) {
      throw new Error('Age must be a positive number')
    }
  }
}

Sanitation

You can also ‘santizize’ the user input data like so. Mongoose Sanitation Docs

name: {
  type: String,
  lowercase: true,
  trim: true
}

Structuring a REST API

REST (Representational State Transfer) Application Programming Interface REST API or Restful API. An API is a set of tools that allows you to build applications. REST allows clients like web applications to access and manipulate resources using a set of predefined operations. IE. The ability to create a new task, or mark one as complete, upload a profile photo for a user account, etc. All this happens through http requests from the client to the server to get or send information.

The task resource

Create - POST - /tasks
Read - GET - /tasks (multiple)
Read - GET - /tasks/:id (singular)
Update - PATCH - /tasks/:id
Delete - DELETE - /tasks/:id

HTTP Request Structure
POST /tasks HTTP/1.1
Accept: application/json
Connection: Keep-alive
Authorization: Bearer edgrefrdoergerf...

//JSON request body
{"Description: "Order new drill bits"}
HTTP Response Structure
HTTP/1.1 201 created
Date: Mon, 1 Jul 2019 15:37:37 GMT
Server: Express
Content-Type: application/json

{"_id": "c35fssa53efrfgsdf", "Description":"Get drill bits", "completed" : false}

Integrating it all (Mongo, Express, Mongoose)

Setting up Express
//src/index.js
const express = require('express')

const app = express();
const port = process.env.PORT || 3000

app.listen(port, () => {
  console.log('Server is up on port ' + port)
})
Creating the scripts
// package.json
"scripts": {
  "start": "node src/index.js",
  "dev": "nodemon src/index.js"
},
Tell express to parse incoming json data & set up POST listener

app.use(express.json()) causes data sent to the path to be parsed as json. We then setup an app.post to listen to anything sent to ‘/users’

//setup server to auto parse json coming into a object
app.use(express.json())

//app.post(path,callback)
app.post('/users', (req,res) => {
  console.log(req.body)
  res.send('testing')
})
Setup a models directory in SRC and add user.js to it
const User = mongoose.model('User', {
  name: {
    type: String,
    required: true,
    trim: true
  },
  email: {
    type: String,
    required: true,
    trim: true,
    lowercase: true,
    validate(value) {
      if(!validator.isEmail(value)) {
        throw new Error('Email is invalid')
      }
    }
  },
  password: {
    type: String,
    required: true,
    minlength: 7,
    trim: true,
    validate(value) {
      if (validator.contains(value.toLowerCase(),'password')) {
        throw new Error('Error! Password cannot contain "password"!')
      }
    }
  },
  age: {
    type: Number,
    default: 0,
    validate(value) {
      if (value < 0 ) {
        throw new Error('Age must be a positive number')
      }
    }
  }
});
Adding mongoose to index.js

Since we already connect to the mongodb in mongoose.js, we can just require that file to run in the index.js file. We also need to require the newly setup user.js model in order to create users when ‘/users’ is POSTed to.

//this will ensure that the mongoose.js file in /db will run and connect to the database
require('./db/mongoose')
const User = require('./models/user')

...

Setup the POST method

Using the User model and info sent to us through the POST method, the following code will send the data through mongoose to our mongo database, creating a user if all was entered correctly. If an error occurs, it is important to set the status code appropriately, to give user context of what happened. http status code resource

app.post('/users', (req,res) => {
  const user = new User(req.body)
  user.save().then(() => {
    res.status(201).send(user)
  }).catch((e) => {
    res.status(400).send(e)
  })
})

Setting up the GET methods to get users/tasks using mongoose

We will use mongoose to fetch everything from the database. mongoose queries guide These provide helper functions for CRUD operations using the model with the method on it. IE. User.find()

app.get('/users', (req,res) => {
  //fetches all users in db
  User.find({}).then((users) => {
    res.send(users)
  }).catch((e) => {
    //internal server error if it cant get users
    res.staus(500).send()
  })
})

To get a single user we will set it up a bit differently. We also have to take into consideration that User.find and User.findById do not consider it an ‘Error’ when they do not find anything in the database. They just return nothing.

app.get('/users/:id', (req,res) => {
  const _id = req.params.id

  User.findById(_id).then((user) => {
    if (!user) {
      return res.status(404).send()
    }

    res.send(user)
  }).catch((e) => {
    res.status(500).send()
  })
});
Back to blog...