NodeJS 8 from scratch — Part 6.4 — Api Development

Anuj Baranwal
4 min readJul 23, 2018

In previous parts, we discussed about —

  • basics of nodeJS
  • debugging
  • asynchronous programming
  • call stack and event loop
  • promises
  • express and deployment
  • testing with mocha and supertest
  • POST Api in NodeJS and testing

Part 1 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-1-a3c1431f1e15

Part 2 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-2-3035f8f46b09

Part 3 — https://medium.com/@anujbaranwal/nodejs-from-scratch-part-3-20956ec252a3

Part 4 — https://medium.com/@anujbaranwal/nodejs-from-scratch-part-4-d0aadf019c79

Part 5 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-5-3d3e3a84b64

Part 6.1 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-1-api-development-5dee11785d62

Part 6.2 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-2-api-development-f92f76eb3521

Part 6.3 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-3-api-development-9b046fed7364

Repo — https://github.com/anuj070894/nodejs_medium_2

In the previous part of api development(6.1, 6.2, 6.3), we learned how to write basic crud operations with a npm module called “mongodb” and Post api in express and deploying with an actual mongodb instance in . In this part, we will complete with UPDATE and DELETE requests and their test cases and creating a test database and development database separate.

Let’s start by seeing how we can delete a resource from mongodb in nodeJS

Deleting a resource

in playground/mongoose-remove.js

const {ObjectID} = require('mongodb');const {mongoose} = require('../server/db/mongoose');const {Todo} = require('../server/models/todo');Todo.remove({}).then((result) => {console.log("Delete All: ", result); // { n: 3, ok: 1 }}).catch((e) => {console.log(e);});Todo.findOneAndRemove({_id: "5b55a4c21a4e48bcd9199621"}).then((result) => {console.log("FindOneandRemove: ", result); // actual record, otherwise null}).catch((e) => {console.log(e);});Todo.findByIdAndRemove("5b558284986aada89ed7976f").then((result) => {console.log("FindByIdAndRemove: ", result); // actual record, otherwise null in case of no records found}).catch((e) => {console.log(e);});

DELETE /todos/:id

Now we need to integrate the delete end point in our nodeJS application.

in server/server.js

...
app.delete('/todos/:id', (req, res) => {
const id = req.params.id;if (!ObjectID.isValid(id)) {return res.status(400).send();}Todo.findByIdAndRemove(id).then((result) => {if (!result) {return res.status(404).send();}res.send({result});}).catch((e) => {res.status(400).send();});});...

We need to write the test cases for the delete functionality in our app.

in server/tests/server.test.js

...
describe('DELETE /todos/:id', () => {
it('shooud remove a todo', (done) => {const id = todos[0]._id.toHexString();request(app).delete(`/todos/${id}`).expect(200).expect((res) => {expect(res.body.result._id).toBe(id);}).end((err, result) => {if (err) {return done(err);}Todo.findById(id).then((result) => {expect(result).toNotExist();done();}).catch((e) => {done(e);});});});it('should return 404 if the id is not found', (done) => {const id = 123;const text = todos[0].text;request(app).delete(`/todos/${id}`).expect(400).end(done);});it('should return 404 if the record is not found', (done) => {const id = '5b558284986aada89ed7976a';const text = todos[0].text;request(app).delete(`/todos/${id}`).expect(404).end(done);});...

Branch — 08_Init

Now the last of the CRUD operation is the update.

UPDATE /todos/:id

To do an update, as we have seen earlier in parts, that it requires many additional parameters to the call. We will also be making the use of a library called as lodash which provides some really awesome javascript utilities.

To install lodash, run — npm install --save lodash

in server/server.js

app.patch('/todos/:id', (req, res) => {const id = req.params.id;if (!ObjectID.isValid(id)) {return res.status(400).send();}const body = _.pick(req.body, ['text', 'completed']);if (_.isBoolean(body.completed) && body.completed) {body.completedAt = new Date().getTime(); // if we get a flag for whether it is completed, then we should update the completedAt time} else {body.completedAt = null; // otherwise set it to defaultsbody.completed = false;}Todo.findByIdAndUpdate(id, {$set: body // this is an object. it updates all the attributes in the body accordingly}, {new: true}).then((result) => {if (!result) {return res.status(404).send();}res.send({result});}).catch((e) => {res.status(400).send();});});

Finally, we will write the test cases for the route that we have just written.

in server/tests/server.test.js

...
describe('UPDATE /todos/:id', () => {
it('shooud update a todo', (done) => {const id = todos[0]._id.toHexString();const text = 'Todo100';request(app).patch(`/todos/${id}`).send({text,completed: true}).expect((res) => {expect(res.body.result.text).toBe(text);expect(res.body.result.completed).toBe(true);}).end((err, result) => {if (err) {return done(err);}Todo.findById(id).then((result) => {expect(result.text).toBe(text);expect(result.completed).toBe(true);done();}).catch((e) => {done(e);});});});it('should clear completedAt when todo is not completed', (done) => {const id = todos[0]._id;request(app).patch(`/todos/${id}`).send({text: "Sample text",completed: false}).expect((res) => {expect(res.body.result.completedAt).toNotExist();}).end(done);});});...

That’s all for the test cases that we will be writing for the update operation in our route.

Now, we have completed all the Create, Read, Update and Delete routes in our express based node application along with the test cases. One final thing that we have to do is to create a test database for the test cases that we have written and make the current database that we have in robomongo as our development database. Let’s see how we are gonna do that.

Creating a Test database

Currently, in server/db/mongoose.js , we either have our development database (or) the production database. To make sure that it uses the test database, when we run the tests, we can make use of the process.env.NODE_ENV setting which is set according to the environment in which the app is running.

In production, heroku sets process.env.NODE_ENV to production

in package.json

"test": "export NODE_ENV=test || set \"NODE_ENV=test\" && mocha server/**/*.test.js",

We are explicitly setting the NODE_ENV to test

const env = process.env.NODE_ENV || 'development';// NODE_ENV will be set to 'production' by the heroku. And we have written the command to set it to 'test' when we are running the tests. So in other cases, just default it to 'development'if (env === 'development') {process.env.port = 3000;process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoApp';} else if (env === 'test') {process.env.port = 3000;process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoAppTest'; // a new test database}const port = process.env.PORT;const {mongoose} = require('./db/mongoose');app.use(bodyParser.json());
...

Branch — 09_Init

So, this is how we can configure our test database that we will use only for testing. We have covered the delete and patch routes in our express application along with the test cases.

This completes our four part series of api development and testing with nodeJS with mongoDB. Hope, this is helpful. See ya in next part :)

--

--

Anuj Baranwal

Full Stack Web Developer @AdobeSystems Bangalore. Sometimes I code, I draw, I play, I edit and write