Koa.js - Database



We are receiving the requests, but are not storing them anywhere. We need a Database to store the data. We'll use a famous NoSQL database called MongoDB. To install and read about Mongo, head over to this link.

In order to use Mongo with Koa, we need a client API for the node. There are multiple options for us, however for this tutorial we'll stick to mongoose. Mongoose is used for document modeling in Node for MongoDB. Document modeling means that, we will create a Model (much like a class in document-oriented programming), and then we will produce documents using this Model (like we create documents of a class in OOP). All our processing will be done on these "documents", then finally, we will write these documents in our database.

Setting Up Mongoose

Now that we have Mongo installed, let us install mongoose, the same way we have been installing our other node packages.

$ npm install --save mongoose

Before we start using mongoose, we have to create a database using the Mongo shell. To create a new database, open your terminal and enter "mongo". A Mongo shell will start, enter the following.

use my_db

A new database will be created for you. Whenever you open the Mongo shell, it'll default to "test" db and you'll have to change to your database using the same command as above.

To use mongoose, we will require it in our app.js file and then connect to the mongod service running on mongodb://localhost

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

app.use(_.routes());
app.listen(3000);

Now our app is connected to our database, let’s create a new Model. This model will act as a collection in our database. To create a new Model, use the following code, before defining any routes.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.use(_.routes());
app.listen(3000);

The above code defines the schema for a person and is used to create a mongoose Model Person.

Saving Documents

Now we will create a new html form, which will get the details of a person and save it to our database. To create the form, create a new view file called person.pug in the views directory with the following content.

html
   head
      title Person
   body
      form(action = "/person", method = "POST")
         div
            label(for = "name") Name: 
            input(name = "name")
         br
         div
            label(for = "age") Age: 
            input(name = "age")
         br
         div
            label(for = "nationality") Nationality: 
            input(name = "nationality")
         br
         button(type = "submit") Create new person

Also add a new get route in index.js to render this document.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.get('/person', getPerson);

function *getPerson(next){
   this.render('person');
   yield next;
}

app.use(_.routes());
app.listen(3000);

Go to localhost:3000/person to check if our form is displaying right. Note that this is just the UI, it’s not working yet. This is how our form looks.

Mongoose Create

We'll now define a post route handler at '/person' which will handle this request.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.post('/person', createPerson);

function *createPerson(next){
   var self = this;
   var personInfo = self.request.body; //Get the parsed information
   
   if(!personInfo.name || !personInfo.age || !personInfo.nationality){
      self.render(
         'show_message', {message: "Sorry, you provided wrong info", type: "error"});
   } else {
      var newPerson = new Person({
         name: personInfo.name,
         age: personInfo.age,
         nationality: personInfo.nationality
      });
      yield newPerson.save(function(err, res) {
         if(err)
            self.render('show_message', 
               {message: "Database error", type: "error"});
         else
            self.render('show_message', 
               {message: "New person added", type: "success", person: personInfo});
      });
   }
}

app.use(_.routes());
app.listen(3000);

In the above code, if we receive any empty field or do not receive any field, we will send an error response. However, if we receive a well-formed document, then we create a newPerson document from the Person model and save it to our DB using newPerson.save() function. This is defined in mongoose and accepts a callback as argument. This callback has two arguments, error and response. This will render show_message view, so we need to create that as well.

To show the response from this route, we will also need to create a show_message view. Create a new view with the following code.

html
   head
      title Person
   body
      if(type = "error")
         h3(style = "color:red") #{message}
      else
         h3 New person, name: 
            #{person.name}, age: 
            #{person.age} and nationality: 
            #{person.nationality} added!

Following is the response we receive on successfully submitting the form (show_message.pug).

Mongoose Response

We now have an interface to create persons!

Retrieving Documents

Mongoose provides a lot of functions for retrieving documents, we will focus on three of those. All these functions also take a callback as the last parameter, and just like the save function, their arguments are error and response.

The three functions are −

Model.find(conditions, callback)

This function finds all the documents matching the fields in conditions object. Same operators used in Mongo also work in mongoose. For example, this will fetch all the documents from the persons’ collection.

Person.find(function(err, response){
   console.log(response);
});

This will fetch all documents where the field name is "Ayush" and age is 20.

Person.find({name: "Ayush", age: 20}, 
   function(err, response){
      console.log(response);
   });

We can also provide the projection we need, i.e., the fields we need. For example, if we want only the names of the people whose nationality is "Indian", we use −

Person.find({nationality: "Indian"}, 
   "name", function(err, response) {
      console.log(response);
   });

Model.findOne(conditions, callback)

This functions always fetches a single, most relevant document. It has the same exact arguments as Model.find().

Model.findById(id, callback)

This function takes in the _id (defined by mongo) as the first argument, an optional projection string and a callback to handle the response. For example,

Person.findById("507f1f77bcf86cd799439011", 
   function(err, response){
      console.log(response);
   });

Let's create a route to view all people records.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.get('/people', getPeople);
function *getPeople(next){
   var self = this;
   
   yield Person.find(function(err, response){
      self.body = response;
   });
}
app.use(_.routes());
app.listen(3000);

Updating Documents

Mongoose provides three functions to update documents.

Model.update(condition, updates, callback)

This function takes a condition and an updates the object as input and applies the changes to all the documents matching the conditions in the collection. For example, the following code will update all Person documents to have a nationality "American".

Person.update({age: 25},
   {nationality: "American"}, 
   function(err, response){
      console.log(response);
   });

Model.findOneAndUpdate(condition, updates, callback)

It does exactly what is says. Finds one document based on the query and updates that according to the second argument. It also takes a callback as the last argument. For example,

Person.findOneAndUpdate({name: "Ayush"}, 
   {age: 40}, 
   function(err, response){
      console.log(response);
   });

Model.findByIdAndUpdate(id, updates, callback)

This function updates a single document identified by its id. For example,

Person.findByIdAndUpdate("507f1f77bcf86cd799439011", 
   {name: "James"}, 
   function(err, response){
      console.log(response);
   });

Let’s create a route to update people. This will be a PUT route with the id as a parameter and details in the payload.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.put('/people/:id', updatePerson);

function *updatePerson() {
   var self = this;
   yield Person.findByIdAndUpdate(self.params.id, 
      {$set: {self.request.body}}, function(err, response){
      
      if(err) {
         self.body = {
            message: "Error in updating person with id " + self.params.id};
      } else {
         self.body = response;
      }
   });
}

app.use(_.routes());
app.listen(3000);

To test this route, enter the following in your terminal (replace the id with an id from your created people).

curl -X PUT --data "name = James&age = 20&nationality = American" https://localhost:3000/people/507f1f77bcf86cd799439011

This will update the document associated with the id provided in the route with the above details.

Deleting Documents

We have covered Create, Read and Update, now we'll see how mongoose can be used to Delete documents. There are three functions here, exactly like update.

Model.remove(condition, [callback])

This function takes a condition object as input and removes all the documents matching the conditions. For example, if we need to remove all people aged 20,

Person.remove({age:20});

Model.findOneAndRemove(condition, [callback])

This functions removes a single, most relevant document according to conditions object. For example,

Person.findOneAndRemove({name: "Ayush"});

Model.findByIdAndRemove(id, [callback])

This function removes a single document identified by its id. For example,

Person.findByIdAndRemove("507f1f77bcf86cd799439011");

Now let’s create a route to delete people from our database.

var koa = require('koa');
var _ = require('koa-router')();
var app = koa();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

_.delete('/people/:id', deletePerson);
function *deletePerson(next){
   var self = this;
   yield Person.findByIdAndRemove(self.params.id, function(err, response){
      if(err) {
         self.body = {message: "Error in deleting record id " + self.params.id};
      } else {
         self.body = {message: "Person with id " + self.params.id + " removed."};
      }
   });
}

app.use(_.routes());
app.listen(3000);

To test this out, use the following curl command −

curl -X DELETE https://localhost:3000/people/507f1f77bcf86cd799439011

This will remove the person with the given id producing the following message. −

{message: "Person with id 507f1f77bcf86cd799439011 removed."}

This wraps up how we can create simple CRUD applications using MongoDB, mongoose and Koa. To explore mongoose further, read the API docs.

Advertisements