Build a To-Do API With Node, Express, and MongoDB

API stands for Application Programming Interface. APIs allow the creation of an application to access features of an application or service. Building APIs with Node is very easy. Yes, you heard me right!

Build a To-Do API With Node, Express, and MongoDB

In this tutorial, you will be building a To-Do API. After you are done here, you can go ahead to build a front end that consumes the API, or you could even make a mobile application. Whichever you prefer, it is completely fine.

Application Setup

To follow this tutorial, you must have Node and NPM installed on your machine.

Mac users can make use of the command below to install Node.

brew install node

Windows users can hop over to the Node.js download page to download the Node installer.

Ubuntu users can use the commands below:

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

To show that you have Node installed open your terminal and run node -v. You should get a prompt telling you the version of Node you have installed.

You do not have not install NPM; it comes with Node. To prove that, run npm -v from your terminal and you will see the version you have installed.

Now go and create a directory where you will be working from, and navigate into it.

mkdir node-todo-api
cd node-todo-api

Initialize npm in the current working directory by running:

npm init -y

The -y flag tells npm to initialize using the default options.

That will create a package.json file for you. It is time to start downloading all the packages you will make use of. NPM makes this hassle free. You need them as dependencies.

To download these packages, run:

npm install express body-parser lodash mongoose mongodb --save

The --save flag tells npm to install the packages as dependencies for your application.

When you open your package.json file, you will see what I have below:

#package.json

  "dependencies": {
    "body-parser": "^1.17.2",
    "express": "^4.15.3",
    "lodash": "^4.17.4",
    "mongodb": "^2.2.29",
    "mongoose": "^4.11.1"
  },

Before you start coding, you have to install MongoDB on your machine if you have not done that already. Here is a standard guide to help you in that area. Do not forget to return here when you are done.

Create the Todo Model

Create a folder called server, and inside it create another folder called models. This is where your Todo model will exist. This model will show how your Todo collection should be structured.

#server/models/todo.js

const mongoose = require('mongoose') // 1

const Todo = mongoose.model('Todo', { // 2
  title: {           // 3
    type: String,
    required: true,
    minlength: 1,
    trim: true
  },
  completed: { // 4
    type: Boolean,
    default: false
  },
  completedAt: { // 5
    type: Number,
    default: null
  }
})

module.exports = {Todo}  // 6
  1. You need to require the mongoose package you installed using NPM.
  2. You create a new model by calling the model function of mongoose. The function receives two parameters: the name of the model as a string, and an object which contains the fields of the model. This is saved in a variable called Todo.
  3. You create a text field for your to-do. The type here is String, and the minimum length is set at 1. You make it required so that no to-do record can be created without it. The trim option ensures that there are no white spaces when records are saved.
  4. You create a field to save true of false value for each to-do you create. The default value is false.
  5. Another field is created to save when the to-do is completed, and the default value is null.
  6. You export the Todo module so it can be required in another file.
Set Up Mongoose

Inside your server folder, create another folder called db and a file called mongoose.js.

This file will be used to interact with your MongoDB, using Mongoose. Make it look like this.

#server/db/mongoose.js

const mongoose = require('mongoose') // 1

mongoose.Promise = global.Promise   // 2
mongoose.connect(process.env.MONGODB_URI) // 3

module.exports = {mongoose} // 4
  1. You require the mongoose package you installed.
  2. Plugs in an ES6-style promise library.
  3. You call the connect method on mongoose, passing in the link to your MongoDB database. (You will set that up soon.)
  4. You export mongoose as a module.

Set Up the Configuration

Time to set up a few configurations for your API. The configuration you will set up here will be for your development and test environment (you will see how to test your API).

#server/config/config.js

let env = process.env.NODE_ENV || 'development'   // 1

if (env === 'development') {  // 2
  process.env.PORT = 3000
  process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoApp'
} else if (env === 'test') {  // 3
  process.env.PORT = 3000
  process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoAppTest'
}
  1. You create a variable and store it in the Node environment or the string development.
  2. The if block checks if env equals development. When it does, the port is set to 3000, and the MongoDB URI is set to a particular database collection.
  3. If the first condition evaluates to false and this is true, the port is set to 3000, and a different MongoDB database collection is used.
Create End Points
#server/server.js

require('./config/config')   // 1

const _ = require('lodash')   // 2
const express = require('express') // 3
const bodyParser = require('body-parser')  // 4
const {ObjectId} = require('mongodb')   // 5

const {mongoose} = require('./db/mongoose')   // 6
const {Todo} = require('./models/todo')   // 7

const app = express()   // 8
const port = process.env.PORT || 3000  // 9

app.use(bodyParser.json())  // 10

app.post('/todos', (req, res) => {
  let todo = new Todo({
    text: req.body.text
  })

  todo.save().then((doc) => {
    res.send(doc)
  }, (e) => {
    res.status(400).send(e)
  })
})
  1. Requires the configuration file created earlier.
  2. Requires lodash installed with NPM.
  3. Requires express installed with NPM.
  4. Requires bodyParser package.
  5. Requires ObjectId from MongoDB.
  6. Requires the mongoose module you created.
  7. Requires your Todo model.
  8. Sets app to the express module imported.
  9. Sets port to the port of the environment where the application will run or port 3000.
  10. Sets up middleware to make use of bodyparser.

In the next part, you created the post request, passing in the path and a callback function which has request and response as its parameters.

In the block of code, you set todo to the text that is passed in the body of the request, and then call the save function on the todo. When the todo is saved, you send the HTTP response—this time, the todo. Otherwise, the status code 400 is sent, indicating an error occurred.

#server.js

...

// GET HTTP request is called on /todos path
app.get('/todos', (req, res) => {

  // Calls find function on Todo
  Todo.find().then((todos) => {

    // Responds with all todos, as an object
    res.send({todos})

    // In case of an error
  }, (e) => {

    // Responds with status code of 400
    res.status(400).send(e)
  })
})

// GET HTTP request is called to retrieve individual todos
app.get('/todos/:id', (req, res) => {

  // Obtain the id of the todo which is stored in the req.params.id
  let id = req.params.id
 
  // Validates id
  if (!ObjectId.isValid(id)) {

    // Returns 400 error and error message when id is not valid
    return res.status(404).send('ID is not valid')
  }

  // Query db using findById by passing in the id retrieve
  Todo.findById(id).then((todo) => {

    // If no todo is found with that id, an error is sent
    if (!todo) {
      return res.status(404).send()
    }

    // Else the todo retrieve from the DB is sent.
    res.send({todo})

    // Error handler to catch and send error
  }).catch((e) => {
    res.status(400).send()
  })
})

// HTTP DELETE request routed to /todos/:id
app.delete('/todos/:id', (req, res) => {

  // Obtain the id of the todo which is stored in the req.params.id
  let id = req.params.id

  // Validates id
  if (!ObjectId.isValid(id)) {

    // Returns 400 error and error message when id is not valid
    return res.status(404).send()
  }

  // Finds todo with the retrieved id, and removes it
  Todo.findByIdAndRemove(id).then((todo) => {

    // If no todo is found with that id, an error is sent
    if (!todo) {
      return res.status(404).send()
    }

    // Responds with todo
    res.send({todo})

    // Error handler to catch and send error
  }).catch((e) => {
    res.status(400).send()
  })
})

// HTTP PATCH requested routed to /todos/:id
app.patch('/todos/:id', (req, res) => {

  // Obtain the id of the todo which is stored in the req.params.id
  let id = req.params.id

  //  Creates an object called body of the picked values (text and completed), from the response gotten
  let body = _.pick(req.body, ['text', 'completed'])

    // Validates id
    if (!ObjectId.isValid(id)) {
      // Returns 400 error and error message when id is not valid
      return res.status(404).send()
  }

  // Checks if body.completed is boolean, and if it is set
  if (_.isBoolean(body.completed) && body.completed) {

    // Sets body.completedAt to the current time
    body.completedAt = new Date().getTime()
  } else {

    // Else body.completed is set to false and body.completedAt is null
    body.completed = false
    body.completedAt = null
  }

  // Finds a todo with id that matches the retrieved id.
  // Sets the body of the retrieved id to a new one
  Todo.findOneAndUpdate(id, {$set: body}, {new: true}).then((todo) => {

    // If no todo is found with that id, an error is sent
    if (!todo) {
      return res.status(404).send()
    }

    // Responds with todo
    res.send({todo})

    // Error handler to catch and send error
  }).catch((e) => {
    res.status(400).send()
  })

})

// Listens for connection on the given port
app.listen(port, () => {
  console.log(`Starting on port ${port}`)
})

// Exports the module as app.
module.exports = {app}

You have created HTTP methods covering all parts of your API. Now your API is ready to be tested. You can do test it using Postman. If you do not have Postman installed on your machine already, go ahead and get it from the Postman website.

Start up your node server using node server.js

Open up postman and send an HTTP POST request. The specified URL should be http://locahost:3000/todos.

For the body, you can use this:

{
  "text": "Write more code",
  "completed" : "false"
}

And you should get a response. Go ahead and play around with it.

Conclusion

In this tutorial, you learned how to build an API using Node. You made use of a lot of resources to make the API a powerful one. You implemented the necessary HTTP methods that are needed for CRUD operation.

JavaScript has become one of the de facto languages of working on the web. It’s not without its learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available in the ThemeKeeper marketplace.

As you continue to build in Node, you will get to understand its power and the reason why it is used worldwide.