In one of our previous MEAN Stack tutorial, we followed a step by step approach to develop a RESTful API using ExpressJS with MongoDB. Here in this MEAN Stack article, we are going to develop another application in ExpressJS and MongoDB with ReactJS. We will follow the same step by step approach to build this application. If you are not familiar with the technologies being used in this article, you can follow the below links to fully understand these technologies as:
- MEAN Stack Tutorial
- ExpressJS – Beginner to Professional
- Getting Started with MongoDB
- Must Have ReactJS – All you need to know
iOS and Android App Development from scratch – build fully native mobile apps ridiculously fast! Get up and running with React Native quickly, and teach the core knowledge you need to deeply understand and build React components for mobile devices.
Master the advanced topics of React Native: Animations, Maps, Notifications, Navigation and More! Go beyond the basics of React Native! This course will teach you the advanced topics you need to make a #1 best-selling app.
STEP 1: Initialize the project:
- Create a folder with the project name such as todoapp.
- Open command prompt window on the project folder.
- Run the following command
1npm init
STEP 2: Install Express:
- Install express with following command
1npm install express --save
STEP 3: Install Express Generator:
- Install express-generator using following command
1npm install express-generator --save
STEP 4: Create the Project:
- Use the following command to create the express project.
1express hello-mern
This will give the following output:
1234567891011121314151617181920212223create : todoappcreate : todoapp/package.jsoncreate : todoapp/app.jscreate : todoapp/publiccreate : todoapp/routescreate : todoapp/routes/index.jscreate : todoapp/routes/users.jscreate : todoapp/viewscreate : todoapp/views/index.jadecreate : todoapp/views/layout.jadecreate : todoapp/views/error.jadecreate : todoapp/public/stylesheetscreate : todoapp/public/stylesheets/style.csscreate : todoapp/public/javascriptscreate : todoapp/public/imagescreate : todoapp/bincreate : todoapp/bin/wwwinstall dependencies:$ cd hellomern && npm installrun the app:$ DEBUG=hellomern ./bin/www
STEP 5: Setup Development Dependencies:
- Add the following dependencies and dev dependencies in package.json file and run npm install from the root folder of the project.
123456789101112131415161718192021222324252627282930313233343536{"name": "hello-mern","version": "0.0.0","private": true,"scripts": {"start": "webpack --progress --colors --watch -d","build": "webpack --progress --colors -p"},"dependencies": {"axios": "^0.15.3","babel-cli": "^6.11.4","babel-core": "^6.13.2","babel-preset-es2015": "^6.13.2","babel-preset-react": "^6.11.1","body-parser": "~1.16.0","cookie-parser": "~1.4.3","debug": "~2.6.0","ejs": "^2.5.6","express": "~4.14.1","jade": "~1.11.0","mongojs": "^2.4.0","morgan": "~1.7.0","react": "^15.4.2","react-bootstrap": "^0.30.7","react-dom": "^15.4.2","react-router": "^2.6.1","serve-favicon": "~2.3.2"},"devDependencies": {"babel-loader": "^6.2.10","http-server": "^0.9.0","webpack": "^1.13.3"}}
STEP 6: Setup ReactJs Build Dependencies:
- In package.json add the following script and dev-dependencies.
12345678910"scripts": {"start": "node ./bin/www","start": "webpack --progress --colors --watch -d","build": "webpack --progress --colors -p"},"devDependencies": {"babel-loader": "^6.2.10","http-server": "^0.9.0","webpack": "^1.13.3"}
- Config the webpack.config.js file as following:
123456789101112131415161718192021222324var webpack = require('webpack');var definePlugin = new webpack.DefinePlugin({__DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),__PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))});var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');module.exports = {cache: true,entry: { main: './views/index.jsx' },output: { path: 'public/build', filename: '[name].js' },module: {loaders: [{test: /\.jsx?$/, loader: 'babel', exclude: /(node_modules|bower_components)/, query: { presets: ['react', 'es2015'] }},]},resolve: {extensions: ['', '.js', '.jsx']},plugins: [definePlugin,commonsPlugin]};
- From command prompt run ‘npm start’. This will create a new directory called build inside public folder and the following files will be there:
- Create index.html file inside public folder.
1234567891011121314151617181920<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"><title>MERN</title><meta name="viewport" content="width=device-width"><link rel="stylesheet" href="./stylesheets/style.css" /><link rel="stylesheet" href="./stylesheets/App.css" /><script src="./build/common.js"></script></head><body><div id="root"></div><script src="./build/main.js"></script></body></html>
- Change the routes in routes/index.js as following:
12345678910var express = require('express');var router = express.Router();/* GET home page. */router.get('/', function(req, res, next) {// res.render('index', { title: 'Express'}); this is the old lineres.render('index.html');});module.exports = router;
- Change the app-engine in app.js.
1234// view engine setupapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'ejs');app.engine('html', require('ejs').renderFile);
- Create a index.jsx in /views folder.
123456789import React from 'react';import ReactDOM from 'react-dom';import App from './App';ReactDOM.render(<App />,document.getElementById('root'));
- Create a App.jsx in /views folder.
123456789101112131415161718import React, { Component } from 'react';class App extends Component {render() {return (<div className="App"><div className="App-header"><img src="./images/logo.svg" className="App-logo" alt="logo" /><h2>Welcome to React+ Express +Mongo</h2></div></div>);}}export default App;
- From browser run http://localhost:3000/
Follow here for a complete ASP.NET Core 2.0 MongoDB solution.
STEP 7: Run Mongo:
- Run mongodb.exe it will start the mongodb.
- Connect to todoapp databse using following command:
STEP 8: Develop the Server-side:
- Create the server REST API in routes/todo.js
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182var express = require('express');var router = express.Router();var mongojs = require('mongojs');var db = mongojs('mongodb://localhost:27017/todoapp', ['todos']);// Get All todosrouter.get('/todos', function(req, res, next){db.todos.find(function(err, todos){if(err){res.send(err);}res.json(todos);});});// Get Single Taskrouter.get('/todo/:id', function(req, res, next){db.todos.findOne({_id: mongojs.ObjectId(req.params.id)}, function(err, todo){if(err){res.send(err);}res.json(todo);});});//Save todorouter.post('/new', function(req, res, next){var todo = req.body;if(!todo.title || !(todo.isDone + '')){res.status(400);res.json({"error": "Bad Data"});} else {db.todos.save(todo, function(err, todo){if(err){res.send(err);}res.json(todo);});}});// Delete todorouter.delete('/todo/:id', function(req, res, next){db.todos.remove({_id: mongojs.ObjectId(req.params.id)}, function(err, todo){if(err){res.send(err);}res.json(todo);});});// Update todorouter.put('/todo/:id', function(req, res, next){var todo = req.body;var updtodo = {};if(todo.isDone){updtodo.isDone = todo.isDone;}if(todo.title){updtodo.title = todo.title;}if(!updtodo){res.status(400);res.json({"error":"Bad Data"});} else {db.todos.update({_id: mongojs.ObjectId(req.params.id)},updtodo, {}, function(err, todo){if(err){res.send(err);}res.json(todo);});}});module.exports = router;
STEP 9: Develop the Client-side:
- Add model
12345678910111213141516171819//mode/todo.js'use strict';//import dependencyvar mongoose = require('mongoose');var Schema = mongoose.Schema;//create new instance of the mongoose.schema. the schema takes an object that shows//the shape of your database entries.var TodoSchema = new Schema({title: String,description: String,priority: String,duedate: String,status: String});//export our module to use in server.jsmodule.exports = mongoose.model('Todo', TodoSchema); - Add Form Component ‘TodoForm’.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061import React, { Component } from 'react';import style from './style';export default class TodoForm extends Component {constructor(props) {super(props);this.state = { author: '', text: '' };this.handleSubmit = this.handleSubmit.bind(this);}handleSubmit(e) {e.preventDefault();let author = this.state.author.trim();let text = this.state.text.trim();if (!text || !author) {return;}this.props.onCommentSubmit({ author: author, text: text });this.setState({ author: '', text: '' });}render() {return (<div><h1> Add new Todo</h1><form onSubmit={ this.handleSubmit }><div className="form-group"><label for="exampleInputEmail1">Title</label><input type="text" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Title"/></div><div className="form-group"><label for="exampleSelect1">Priority</label><select className="form-control" id="exampleSelect1"><option>High</option><option>Medium</option><option>Normal</option></select></div><div className="form-group"><label for="exampleTextarea">Description</label><input type="text" className="form-control" id="exampleTextarea"placeholder="Description"/></div><div className="form-group"><label for="datetimepicker1">Due date</label><div className='input-group date' id='datetimepicker1'><input type='text' className="form-control" /><span className="input-group-addon"><span className="glyphicon glyphicon-calendar"></span></span></div></div><button type="submit" className="btn btn-primary">Add</button><button type="cancel" className="btn btn-danger">Cancel</button></form></div>)}} - Add List Component
123456789101112131415161718192021import React, { Component } from 'react';export default class TodoList extends Component {render() {let todoNodes = this.props.data.map(todo => {return (<div className="panel panel-primary">Title : {todo.title}</div>)})return (<div className="panel panel-success"><h1> All Todos </h1>{ todoNodes }</div>)}} - Update App.jsx
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import React, { Component } from 'react';import style from './style';import TodoForm from './TodoForm';import TodoList from './TodoList';import axios from 'axios';class App extends Component {constructor(props) {super(props);this.state = { data: [] };this.loadTodosFromServer = this.loadTodosFromServer.bind(this);this.handleTodoSubmit = this.handleTodoSubmit.bind(this);this.handleTodoDelete = this.handleTodoDelete.bind(this);this.handleTodoUpdate = this.handleTodoUpdate.bind(this);}loadTodosFromServer() {axios.get(this.props.url).then(res => {this.setState({ data: res.data });})}handleTodoSubmit(comment) {let comments = this.state.data;comment.id = Date.now();let newComments = comments.concat([comment]);this.setState({ data: newComments });axios.post(this.props.url+'/todo/'+comment.id, comment).catch(err => {console.error(err);this.setState({ data: comments });});}handleTodoDelete(id) {axios.delete(`${this.props.url}/${id}`).then(res => {console.log('Comment deleted');}).catch(err => {console.error(err);});}handleTodoUpdate(id, comment) {//sends the comment id and new author/text to our apiaxios.put(`${this.props.url}/${id}`, comment).catch(err => {console.log(err);})}componentDidMount() {this.loadTodosFromServer();setInterval(this.loadTodosFromServer, this.props.pollInterval);}render() {return (<div className="App"><div className="App-header"><img src="./images/logo.svg" className="App-logo" alt="logo" /><h2>Welcome to React+ Express +Mongo</h2></div><TodoListonTodoDelete={ this.handleTodoDelete }onTodoUpdate={ this.handleTodoUpdate }data={ this.state.data }/><TodoForm onTodoSubmit={ this.handleTodoSubmit }/></div>);}}export default App; - Update index.jsx
12345678910import React from 'react';import ReactDOM from 'react-dom';import App from './App';ReactDOM.render(<App url='http://localhost:3000/todos'pollInterval={2000}/>,document.getElementById('root')); - From browser
Angular 4 is out now. We can use Angular 4 to develop applications that are cross platform with maximum speed and performance. More on Angular 4 here.
Hopefully, this application will be helpful in understanding the technology in practical manner and developing the first application using React.js with Express and MongoDB.