Click here to Skip to main content
15,867,141 members
Articles / Hosted Services / ExtJS

Part 2: Building web app using react.js, express.js, node.js and mongodb

Rate me:
Please Sign up or sign in to vote.
4.90/5 (20 votes)
31 Dec 2015CPOL6 min read 55K   1.2K   20   15
This is a multi-parts series on building web applications using react.js, node.js, express.js, and mongodb.

Introduction

For quite sometime now, I have been hearing some very interesting things about react.js like how fast it is and how cool it would be to pick react.js for your next web project. All that sounds very good but, unless we try it ourself, we won't be able to appreaciate it. So I decided to play with react.js for a while and see myself how accurate these claims are. Now there is just one problem, react.js documentation assumes certain level of experience with modern Javascript developement workflow, in short let's just say it's not very helpful if you are trying to use it for the first time. This series of articles is my attempt to put all the pieces together and show what it will take to build a fully functional react app. 

In this series of articles, we will be creating a full stack single page JavaScript application from scratch. In Part 1 of this series we will focus completely on react.js workflow and create the front end piece of our app without any backend APIs or database. In the Part 2, we will create backend for our application using express.js and also persist our data in mongodb.

This article is part 2 of this series, if you have not followed the part 1 then you might find this article a bit confusing, please visit the part 1 first.

Setting up express app

We will start exactly from where we left in the part 1. So far we have a fully functional front end react app, next we need to setup our application's backend, so let's start with installing mondodb locally if you do not have that already. I have prepared this demo app on mongodb version 3.0.5 and node.js version 4.2.3, but it should work fine with older versions as well.

Next navigate to the root directory of our app in command shell and run following commands:

C++
npm install mongoose --python=python2.7 --save
npm install body-parser --save
npm install underscore --save

mongoose is a document object mapper, we will use it to interact with mongodb in our application, we have specified python version explicity to avoid installation issues you might face, if there are more than one versions of python installed in your system. body-parser module is needed to parse http request's body since express.js does not have an in built support for this. underscore module has tons of very useful utility functions which make it easy for us to focus on our application's business logic rather than spending too much time writing utility code.

We are now done with the setup so let's start writting some express code.

Implementing REST endpoints

Add two new directories "data" and "controllers" inside "server" directory of your app. As you might have guessed, "data" directory is going to contain mongoose data models and "controllers" directory will have express routers. In our application there is going to be only one model School so let's add a new file "school.js" in data directory and add the following code in it: 

JavaScript
var mongoose = require("mongoose");
var schoolSchema = mongoose.Schema({
    name: String,
    tagline: String
});

module.exports = mongoose.model("school", schoolSchema);

In the above code we have created our very simple model "school" which has only two properties name and tagline of type String. Next let's create our controller, add a new file "schoolController.js" in the controllers directory and add the following code in it:

JavaScript
var mongoose = require("mongoose");
var School = require("../data/school");
var _ = require("underscore");

var router = require("express").Router();
router.route("/schools/:id?").get(getSchools).post(addSchool).delete(deleteSchool);

function getSchools(req, res) {
    School.find(function (err, schools) {
        if (err)
            res.send(err);
        else
            res.json(schools);
    });
}

function addSchool(req, res) {
    var school = new School(_.extend({}, req.body));
    school.save(function (err) {
        if (err)
            res.send(err);
        else
            res.json(school);
    });
}

function deleteSchool(req, res) {
    var id = req.params.id;
    School.remove({ _id: id }, function (err, removed) {
        if (err)
            res.send(err)
        else
            res.json(removed);
    });
}

module.exports = router;

In the above code, we have created a new express router which has only one route "/schools/:id?" and for each HTTP verb we have a seperate request handler. We are going to use only GET, POST and DELETE in our application to keep it simple. Our route also has an optional route parameter which we will need in order to identify specific school while deleting it. To interact with the database for CRUD operation we are using our School model which we had created in the previous step.

Also note that in addSchool and deleteSchool functions, we are accessing req.body object which get's populated by the body-parser middleware. We will configure this in the next step. We are using undercore's extend utility function which copies the req.body object to an empty object to populate our model.

Now let's modify "server.js" file as shown below:

JavaScript
var express = require("express");
var bodyParser = require("body-parser");
var mongoose = require("mongoose");
var path = require("path");

//controllers
var schoolController = require("./controllers/schoolController");

//Express request pipeline
var app = express();
app.use(express.static(path.join(__dirname, "../app/dist")));
app.use(bodyParser.json())
app.use("/api", schoolController);

app.listen(7777, function () {
    console.log("Started listening on port", 7777);
});

// Connect to mongodb database
mongoose.connect("mongodb://localhost/schoolfinder");

We have made few changes in server.js file, we need body-parser and mongoose modules, we have configured bodyparser to parse json payloads on http requests, also we have mounted our schoolController to /api route, so any request in the form of /api/schools or /api/schools/xxxx will be handled by schoolController. Further we need to connect to mongodb database using mongoose.connect function. schoolfinder is our database name which will get created automatically when you insert your first record.

Please ensure that mondodb instance is running before you run the app, you can also change the mongodb connection string to a remote mongo server if you want.

So at this point we have our backend APIs ready, let's revisit the react code to implement REST APIs calls.

Plugging REST to react

We need some library to make ajax calls to backend APIs, I will be using JQuery for this, but you can of cource pick your own favorite. We also need a promise library to avoid nested and painful callbacks. Let's install JQuery and es6-promise modules using npm

npm install jquery --save
npm install es6-promise --save

Next add a new directory "services" inside "app" directory and add a new file "schoolService.js". This file is going to contain REST APIs calls, let's add the folloing code in it:

JavaScript
var $ = require("jquery");
var promise = require("es6-promise");
var resourceUrl = "http://localhost:7777/api/schools";

module.exports = {
    addSchool: function (school) {
        var Promise = promise.Promise;
        return new Promise(function (resolve, reject) {
            $.ajax({
                url: resourceUrl,
                data: JSON.stringify(school),
                method: "POST",
                dataType: "json",
                contentType: "application/json",
                success: resolve,
                error: reject
            });
        });
    },
    getSchools: function () {
        var Promise = promise.Promise;
        return new Promise(function (resolve, reject) {
            $.ajax({
                url: resourceUrl,
                method: "GET",
                dataType: "json",
                success: resolve,
                error: reject
            });
        });
    },
    deleteSchool: function (school) {
        var Promise = promise.Promise;
        return new Promise(function (resolve, reject) {
            $.ajax({
                url: resourceUrl + "/" + school._id,
                method: "DELETE",
                dataType: "json",
                success: resolve,
                error: reject
            });
        });
    }
}

Above code has some very familiar JQuery ajax calls, the only thing I want to highlight is, the way we have used Promises. We attach JQuery's success and error handlers to Promise object's resolve and reject callbacks and we return the Promise object itself. You can find more information about Javascript promises here, I am not going to discuss promises here, it deserves it's own separate article.

Now let's modify "schoolsStore.js" and "main.jsx" files as shown below so that our app starts using the schoolService to handle data rather showing the dummy:

schoolsStore.js

JavaScript
var dispatcher = require("../dispatcher");
var schoolService = require("../services/schoolService");

function SchoolStore() {
    var listeners = [];

    function onChange(listener) {
        getSchools(listener);
        listeners.push(listener);
    }
    
    function getSchools(cb){
        schoolService.getSchools().then(function (res) {
            cb(res);
        });
    }

    function addSchool(school) {
        schoolService.addSchool(school).then(function (res) {
            console.log(res);
            triggerListeners();
        });
    }

    function deleteSchool(school) {
        schoolService.deleteSchool(school).then(function (res) {
            console.log(res);
            triggerListeners();
        });
    }

    function triggerListeners() {
        getSchools(function (res) {
            listeners.forEach(function (listener) {
                listener(res);
            });
        });
    }

    dispatcher.register(function (payload) {
        var split = payload.type.split(":");
        if (split[0] === "school") {
            switch (split[1]) {
                case "addSchool":
                    addSchool(payload.school);
                    break;
                case "deleteSchool":
                    deleteSchool(payload.school);
                    break;
            }
        }
    });

    return {
        onChange: onChange
    }
}

module.exports = SchoolStore();

main.jsx

var React = require("react");
var ReactDOM = require("react-dom");
var SchoolsList = require("./components/SchoolsList.jsx");
var schoolsStore = require("./stores/schoolsStore");
var _schools = [];
var getSchoolsCallback = function(schools){
    _schools = schools;
    render();
};
schoolsStore.onChange(getSchoolsCallback);

function render(){
    ReactDOM.render(<SchoolsList schools={_schools} />, document.getElementById("container"));    
}

Now run the gulp command and browse the app, please ensure you have mongodb instance up and running. Congratulations! there you have a full stack javascript app, 

Summary

As promised in the beginning, we have a finished version of full stack JavaScript app using react.js, express.js, node.js and mongodb. You can extend this app by implementing images upload feature for school that will give you few more areas to explore. I have attatched the finished version of sample code with this article and I encourage you to download and refer to it, in case you face any problems following along. Follow these steps to run the sample:

1. Download and unzip
2. Navigate to the root directory of the extracted app in your command shell
3. Run npm install
4. Run bower install
5. Run gulp
6. Run nodemon .\server\server.js

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
I am a technology lover and software engineer by profession. I enjoy writing code and I work on .NET, JavaScript, NodeJs, Microsoft Azure and other related web technologies.

Comments and Discussions

 
QuestionGot error while run "nodemon .\server\server.js" command Pin
Member 1357629714-Dec-17 19:15
Member 1357629714-Dec-17 19:15 
Questionmain.jsx is rendered or not and how school.js store our data??? Pin
Member 1311749717-Apr-17 0:37
Member 1311749717-Apr-17 0:37 
QuestionUpdating an existing entry Pin
Member 1298818527-Feb-17 16:37
Member 1298818527-Feb-17 16:37 
QuestionRegarding Data Model Pin
Member 1258424114-Jun-16 22:22
Member 1258424114-Jun-16 22:22 
QuestionThank you Pin
Member 125643413-Jun-16 12:05
Member 125643413-Jun-16 12:05 
PraiseReact Express Node and Mongo stack Pin
Member 123724585-Mar-16 22:37
Member 123724585-Mar-16 22:37 
QuestionECONNREFUSED / sockets closed Pin
Member 1234282221-Feb-16 23:05
Member 1234282221-Feb-16 23:05 
I'm a noob, so I don't know if this is my own mistake or what, but on Windows 10 with Mongo 3.2.3, I get an error when trying to start the server. First it said ECONNREFUSED. I changed port 7777 to the "default Mongo port", 27017, and got the message "server localhost:27017 sockets closed".

I tried running it with the zipped files you provided and got the same result.

I loved Part 1, by the way. Thanks!
Questionstmt fails: npm install mongoose --python=python2.7 --save Pin
riixx1-Jan-16 15:58
riixx1-Jan-16 15:58 
AnswerRe: stmt fails: npm install mongoose --python=python2.7 --save Pin
Nishant_Chaturvedi1-Jan-16 17:37
Nishant_Chaturvedi1-Jan-16 17:37 
GeneralRe: stmt fails: npm install mongoose --python=python2.7 --save Pin
riixx8-Jan-16 2:03
riixx8-Jan-16 2:03 
PraiseMy vote of 1 Pin
Theo Boomsma1-Jan-16 4:04
professionalTheo Boomsma1-Jan-16 4:04 
GeneralRe: My vote of 1 Pin
Rohit_Chaturvedi1-Jan-16 4:37
Rohit_Chaturvedi1-Jan-16 4:37 
GeneralRe: My vote of 1 Pin
Theo Boomsma1-Jan-16 14:40
professionalTheo Boomsma1-Jan-16 14:40 
AnswerRe: My vote of 1 Pin
Member 1507871629-Jul-22 11:56
Member 1507871629-Jul-22 11:56 
QuestionVery nice Sir Pin
RNA Team31-Dec-15 19:40
RNA Team31-Dec-15 19:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.