NodeJS 8 from scratch — Part 6.3 — Api Development
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 foundresult
is the record in case the record with that id is present in the mongodbcatch
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