NodeJS 8 from scratch — Part 6.3 — Api Development

Anuj Baranwal
5 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

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

In the previous part of api development(6.1, 6.2), we learned how to write basic crud operations with a npm module called “mongodb” and Post api in express. In this part, we will learn to write GET, UPDATE and DELETE requests and their test cases.

GET /todos

To get all the todos saved in the database

in server/server.js

...// three cases -/*** 1. find works fine but the no results are returned* 2. find works and results are fetched successfully* 3. find fails*/app.get('/todos', (req, res) => {Todo.find().then((results) => {console.log(results);if (!results) {return res.status(404).send();}res.send({results});}).catch((e) => {res.status(404).send();});});...

Testing GET /todos route

in server/tests/server.test.js

...
const todos = [
{"text": "Todo1"}, {"text": "Todo2"}, {"text": "Todo3"}];beforeEach((done) => {Todo.remove({}).then(() => {return Todo.insertMany(todos);}).then(() => {done();}); // removing all the data to give a fresh start});describe('TodoApp Api Tests', () => {describe('GET /todos', () => {it('should get all the todos', (done) => {request(app).get('/todos').expect(200).expect((res) => {expect(res.body.results.length).toBe(3);}).end(done);});});...

Mongoose queries and validation of _id

We will be writing some queries to for the api development in a playground file and see how we can validate the _id when searching for an item in database using _id related query methods.

in playground/mongoose-queries.js

Queries related to using find the records from a collection —

const {ObjectID} = require('mongodb');const {mongoose} = require('../server/db/mongoose');const {Todo} = require('../server/models/todo');Todo.find({_id: "5b558284986aada89ed7976f"}).then((result) => {console.log("Find: ", result); // [] in case of no records found}).catch((e) => {console.log(e);});Todo.findOne({_id: "5b558284986aada89ed7976e"}).then((result) => {console.log("FindOne: ", result); // null in case of no records found}).catch((e) => {console.log(e);});Todo.findById("5b558284986aada89ed7976f").then((result) => {console.log("FindById: ", result); // null in case of no records found}).catch((e) => {console.log(e);});

For validating _id, we need to use the ObjectID from the mongodb module. With findById query, there are actually three cases that can arise —

  • null in case of no record being found
  • result is the record in case the record with that id is present in the mongodb
  • catch block gets executed when the id is not valid
Todo.findById("some id")    .then((result) => {
if (!result) {
return console.log('No such record present with that id');
}
console.log(result); // result is found with the id
})
.catch((e) => {
console.log('In case like id is not valid!!!');
});

However, we can check initially itself if the id is valid or not using the ObjectID from mongodb module.

const {ObjectID} = require('mongodb');
if (!ObjectID.isValid(id)) {
console.log('Id not valid');
}

We will be using this to validate the id when we fetch a todo resource using id. So, let’s do that —

GET /todos/:id — Getting an individual resource

in server/server.js

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

Testing GET /todos/:id — Getting an individual resource

There will be three test cases for the cases that we have mentioned above —

  • should return a todo doc
  • should return a 404 if todo not found
  • should return a 404 for invalid object id

in server/tests/server.test.js

const todos = [{"text": "Todo1","_id": new ObjectID()}, {"text": "Todo2","_id": new ObjectID()}, {"text": "Todo3","_id": new ObjectID()}];...
describe('GET /todos/:id', () => {
it('shooud return a valid todo', (done) => {const id = todos[0]._id;const text = todos[0].text;request(app).get(`/todos/${id}`).expect(200).expect((res) => {expect(res.body.result.text).toBe(text);}).end((err, result) => {if (err) {return done(err);}Todo.findById(id).then((result) => {if (!result) {return done(err);}expect(result.text).toBe(text);done();}).catch((e) => {done(e);});});});it('should return 404 if the id is not valid', (done) => {const id = 123;const text = todos[0].text;request(app).get(`/todos/${id}`).expect(400).end(done);});it('should return 404 if the record is not found', (done) => {const id = '5b558284986aada89ed7976e';const text = todos[0].text;request(app).get(`/todos/${id}`).expect(404).end(done);});});

Branch — 06_Init

So, the last thing that we will be doing in this part is to deploy this api and database in production with heroku

Deploying api and mongodb with heroku

Steps —

  • set to use the environment port variable

const port = process.env.PORT || 3000;

  • in package.json , we have to mention what command to run on start
"start": "node server/server.js"
  • in package.json , we need to tell heroku what node version it should install
"engine": {
"node": "8.10.0"
}
  • to set up the database with heroku, we need to tell heroku that we will be using mongodb based instance in our application —

heroku create

heroku addons:create mongolab:sandbox sandbox is one of the configurations, we can create other mongolab instance that provides much better support. This command however requires you to configure your credit card details even for the free tier account. However, we can use the https://mlab.com/ to create our sandbox account and create a user inside it.

mongodb://<user_name_just_created>:<password_just_created>@ds147461.mlab.com:47461/todoappanujkuma

heroku config

heroku config:set MONGODB_URI=<url above>

This will tell all the configuration variables that you have for heroku. One such variable is MONGODB_URI which will be set up by heroku, the one we can use it inside script to dynamically set it for our mongodb url

mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/TodoApp');

git push heroku master

heroku logs — This is a very useful command as it tells us the server logs

heroku open — open the url in the browser

Branch — 07_Init

We can test it in postman that we are now sending the requests to the heroku url. Just copy the url from the browser<url> and replace it with localhost:3000 in the postman.

There is one last thing that we will be talking in this part — Postman Environments

We can test our requests by configuring the environments variable to change the url according to the environment in which we are testing, i.e., local and prod

That’s all for this part. We will see you in next part. And it’s probably the last part in api development. So see ya then.

In this part, we discussed about —

  • GET routes
  • Deploying in production
  • Deploying mongo instance
  • Postman — configure environments

See ya :)

Part 6.4 — https://medium.com/@anujbaranwal/nodejs-8-from-scratch-part-6-4-api-development-38d600a35ad9

--

--

Anuj Baranwal

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