Introduction

Welcome back to our tutorial on modern Javascript with ES8. Yesterday we configured and scaffolded the groundwork for our two APIs. Now we're going to start on the ES5 version. We have much to cover so let's get started.

Getting Started

So let's open the whole project in our text editor of choice. Once you have done that, we'll begin by writing our app.js file out.

app.js

// import core modules
var express = require('express')
var mongoose = require('mongoose')
var config = require('./config')

// import authentication modules
var passport = require('passport')
var jwt = require('express-jwt')
require('./services/clientAuth')
var authServer = require('./services/oauth').server

// import middlewares
var bodyParser = require('body-parser')
var http = require('http')

// import models
var Client = require('./model/Model').Client

// import controllers
var task = require('./controller/task')
var user = require('./controller/user')

// initialize server
var app = express()

/*
  function that checks if a Client exists in the database for the imaginary dashboard
*/
function clientCheck () {
  Client.findOne({name: 'ToDo Dashboard'}, function (err, client) {
    if (err) {
      console.log(err)
    } else if (!client) {
      console.log('no ToDo Dashboard client found, creating new one.')
      var createClientInfo = {
        name: 'ToDo Dashboard',
        clientId: 'my-awesome-clientid',
        clientSecret: 'my-awesome-clientSecret',
        trustedClient: true
      }
      Client.create(createClientInfo, function (err, createdClient) {
        if (err) {
          console.log('error creating dashboard client!')
        } else {
          console.log('new ToDo Dashboard Client created.')
        }
      })
    }
  })
}

/*
  Configure Mongoose
*/
mongoose.connect(config.mongoUrl, {}, function(err) {
  if (err) {
    console.log(err)
  } else {
    console.log('successfully connected to mongo database')
  }
})

/*
  Server Config
*/

// logger
app.use(function (req, res, next) {
  console.log('method: ' + req.method, 'url: ' + req.url)
  next()
})

// initialize passport
app.use(passport.initialize())

// body parser
app.use(bodyParser.json())

// routing
// create user
app.post('/user/new', user.createUser)
// get user by username
app.get('/user/:username', jwt({secret: config.secret}), user.getUser),
// delete user by username
app.delete('/user', jwt({secret: config.secret}), user.deleteUser),
// get user tasks
app.get('/task/:username', jwt({secret: config.secret}), task.getUserTasks),
// create new task
app.post('/task', jwt({secret: config.secret}), task.createTask),
// edit task
app.put('/task', jwt({secret: config.secret}), task.editTask),
// delete task
app.delete('/task/:username/:taskname', jwt({secret: config.secret}), task.deleteTask)

// start server
var server = http.createServer(app)

// port listener
server.listen(3000, function (err) {
  if (err) {
    console.log(err)
  } else {
    console.log('listening on port', 3000)
  }
})

Good, now let's take a quick assessment of what we have here. First, we are importing our modules the old CommonJS way. Next, you'll see we have a clientCheck() function written using callbacks. It just checks to make sure we have our imaginary dashboard client ready in our database. If not, it creates one. Then, we connect to Mongo using callbacks.

Moving on to the server config, we have our own simple hand written logger using ES5 style string interpolation. Then we initialize passport and a bodyparser. Next, we see our router with JWT middleware to protect certain routes. That said, if you intend to play around with this API in Postman, you may want to remove those JWT middlewares for sanity's sake.

Lastly, you'll see we create our server and have it listen on port 3000. Not too bad.

Now let's write our Model.js file.

var mongoose = require('mongoose')
var Schema = mongoose.Schema
var relationship = require('mongoose-relationship')

// USER Model
var UserSchema = mongoose.Schema({
  username: {type: String, required: true, unique: true},
  password: {type: String, required: true, bcrypt: true},
  tasks: [{type: Schema.ObjectId, ref: 'Task'}]
})

// TASK Model
var TaskSchema = mongoose.Schema({
  name: String,
  user: {type: Schema.ObjectId, ref: 'User', childPath: 'tasks'}
})

// CLIENT Model
var ClientSchema = new mongoose.Schema({
  name: { type: String, unique: true, required: true },
  clientId: { type: String, required: true },
  clientSecret: { type: String, required: true },
  trustedClient: { type: Boolean, required: true }
})

// Plugins
UserSchema.plugin(require('mongoose-unique-validator'))
UserSchema.plugin(require('mongoose-bcrypt'))
TaskSchema.plugin(relationship, {relationshipPathName: 'user'})
ClientSchema.plugin(require('mongoose-unique-validator'))

// Exports
exports.Task = mongoose.model('Task', TaskSchema)
exports.User = mongoose.model('User', UserSchema)
exports.Client = mongoose.model('Client', ClientSchema)

Great, so if you're familiar with normal Javascript code, then there is nothing out of the ordinary here. We imported some modules, defined our Models, added validation, then exported the Models to be used by the rest of our app. So let's move on to our controllers and write them out starting with user.js.

// import models
var User = require('../model/Model').User
var Task = require('../model/Model').Task

// get user by username
exports.getUser = function (req, res) {
  // grab user from database by username
  User.findOne({username: req.params.username}, function (err, user) {
    if (err) {
      console.log(err)
    } else {
      // grab task data for user.id
      Task.find({user: user.id}, function (err, taskList) {
        if (err) {
          console.log(err)
        } else {
          // send proper user info in response
          res.json({username: user.username, tasks: taskList, id: user.id})
        }
      })
    }
  })
}

// create new user
exports.createUser = function (req, res) {
  // format needed data
  var username = req.body.username
  var password = req.body.password
  // create User
  User.create({username: username, password: password}, function (err, user) {
    if (err) {
      console.log(err)
    } else {
      // send response
      res.status(200).send('success')
    }
  })
}

// delete user by username
exports.deleteUser = function (req, res) {
  // get username
  var username = req.body.username
  // delete user by username
  User.findOneAndRemove({username: username}, function (err, deletedUser) {
    if (err) {
      console.log(err)
    } else {
      // send response if all goes well
      res.status(200).send('success')
    }
  })
}

So you'll see here we have imported our Models and then we moved to defining our function calls. We won't dwell on this long. Just note once again the use of callbacks along the way and see if you can spot any opportunities to utilize the ES8 features we had gone over on day one. Now, let's open task.js and write it out.

// import models
var User = require('../model/Model').User
var Task = require('../model/Model').Task

// get all tasks for specific user
exports.getUserTasks = function (req, res) {
  // grab username
  var username = req.params.username
  // find user by username
  User.findOne({username: username}, function (err, user) {
    if (err) {
      console.log(err)
    } else {
      var taskList = []
      for (var i = 0; i < user.tasks.length; i++) {
        Task.findById(id, function (err, newTask) {
          if (err) {
            console.log(err)
          } else {
            taskList.push(newTask)
          }
        })
      }
      // send array of tasks to user
      res.send(taskList)
    }
  })
}

// create new task
exports.createTask = function (req, res) {
  // get username and taskname info
  var username = req.body.username
  var taskname = req.body.taskname
  // find user by username to get the id
  User.findOne({username: username}, function (err, user) {
    if (err) {
      console.log(err)
    } else {
      // create Task
      Task.create({name: taskname, user: user.id}, function (err, newTask) {
        if (err) {
          console.log(err)
        } else {
          // send success signal
          res.json({taskname: newTask.nameid, id: newTask.id})
        }
      })
    }
  })
}

// edit task
exports.editTask = function (req, res) {
  // grab the taskid and taskname
  var taskId = req.body.taskId
  var newTaskName = req.body.newTaskName
  // find the task and update it
  Task.findByIdAndUpdate(taskId, {name: newTaskName}, function (err, task) {
    if (err) {
      console.log(err)
    } else {
      // send success signal
      res.status(200).send('success')
    }
  })
}

// delete task
exports.deleteTask = function (req, res) {
  // grab username and taskname
  var username = req.params.username
  var taskId = req.params.taskId
  // get the user
  User.findOne({username: username}, function (err, user) {
    if (err) {
      console.log(err)
    } else {
      // delete Task
      Task.findByIdAndRemove({taskId: taskId}, function (err, deadTask) {
        if (err) {
          console.log(err)
        } else {
          // edit User
          User.findOneAndUpdate({username: username}, {$pull: {tasks: {$in: [taskId]}}}, function (err, editUser) {
            if (err) {
              console.log(err)
            } else {
              // send success signal
              res.status(200).send('success')
            }
          })
        }
      })
    }
  })
}

As you can see, the callback spaghetti code is strong with this file. Also note the old style for loop. You can especially see the spaghetti nightmare with deleteTask. Not too fun.

Summary

So it's no mystery we just had to write quite a bit of code today. Tomorrow we will finish this monstrosity... I mean API in ES5 then we will begin writing our alternate version in ES8.

Resources

Franzé Jr

Software Engineer with experience working in multi-cultural teams, Franze wants to help people when he can, and he is passionate about programming and Computer Science. Founder of RemoteMeetup.com where he can meet people all over the World. When Franze is not coding, he is studying something about programming.

  1. Comments for Build an API in ES5

You must login to comment

You May Also Like