Getting Started with MERN

Getting Started with MERN

MERN Stack implies the usage of the following technologies for full stack development:

  • MongoDB
  • ExpressJS
  • ReactJS
  • NodeJS

In this post, we’ll go through the basics and logistics of getting started with MERN development. I’ve limited the content of this post to the bare essentials, while learning MERN development from FreeCodeCamp. Resources for where are :

Enviroment Setup

Setting up MongoDB Atlas

For this sample application, we’ll be using Google Cloud Platform to host the database of our application. To connect our app to MongoDB, follow these steps:

  • Go to https://www.mongodb.com/cloud/atlas
  • Sign In/ Create an account
  • Create a new Project
  • Create a new Cluster within the Project
  • Choose Google Cloud Platform as the platform (for this application), and a suitable server location
  • Select the FREE Cluster Tier – you can upgrade later for a larger application database
  • Follow the instructions on the website to setup your cluster and do the following:
    • Create a new user with a password for accessing your cluster
    • Add your IP Address to the list of whitelisted IP Addresses
    • Choose the Connect Your Application option and save the connection string
  • Click on Create Cluster

Creating React Project

After you’ve setup MongoDB Atlas, we’ll setup the local environment for the project. Do the following:

  • Create a root folder for the project. We’ll call it my-mern-app
    1
    2
    $ mkdir my-mern-app
    $ cd my-mern-app
  • Create a react app within the root project folder
    1
    $ npx create-react-app mern-my-mern-app

NOTE : If you already have create-react-app installed globally via npm, this would throw up an error, as global installations of create-react-app are no longer supported. In this case, run the following command instead, which ignores local installations:

1
$ npx --ignore-existing create-react-app mern-my-mern-app

Setting up Environment for Backend

Once the react project has been created, create a folder for backend development within your project root folder. As this is a small project, we’d create this folder in the react app’s folder itself. Run the following commands:

  • Create backend folder
    1
    2
    $ mkdir backend
    $ cd backend
  • Initiate the package.json file for backend, run this in the backend folder
    1
    $ npm init -y
  • Install required npm packages:
    1
    2
    $ npm install express cors mongoose dotenv
    $ npm install -g nodemon

What are these packages?
express - A lightweight web-framework for node
cors - Cross-origin Resource Sharing, allows AJAX requests to skip same origin policy and access remote-hosts, makes accessing stuff outside of our server easy
mongoose - Allows for interaction with MongoDB
dotenv - Loads environment variables, stores in a file
nodemon - facilitates development by restarting app on file changes

Backend

server.js and .env

Create a file server.js in your backend folder and add the following code:

Create a .env file and add your ATLAS_URI to the file. Check MongoDB instructions for more help on this.

Run the following command in your shell to check whether the server is able to establish a connection with MongoDB Atlas:

1
$ nodemon server.js

Models

Inside the backend folder, create another folder named models. Here, we’d create our different models – each file representative of one database model. A sample model is as follows (create a file in models folder – user.model.js)

Model Name : user.model.js

Similarly, other models can be created using the same template.

Routes

Inside the backend folder create a new folder routes. For each model that you created, create a route file here, named after the model. Continuing with the previous example, we’ll create user.js in the routes folder.

Add the following code to the newly created user.js (or other route file respectively):

This code has two endpoints, one for reading the records in the model and one for adding new records to the model.

After this, in the server.js file, add the following lines for each model (I am adding these lines for the user model):

1
2
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);

Additional Routes:

We can additionally create routes to do the following schema for the :

  • update
  • delete
  • findById

A sample for our user schema to do these operations are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// route to get by id
router.route('/:id').get((req, res) => {
User.findById(req.params.id)
.then(user => res.json(user))
.catch(err => res.status(400).json('Error: ' + err));
});

// route to delete by id
router.route('/:id').delete((req, res) => {
User.findByIdAndDelete(req.params.id)
.then(() => res.json('User deleted.'))
.catch(err => res.status(400).json('Error: ' + err));
});

// route to update by id
router.route('/update/:id').post((req, res) => {
User.findById(req.params.id)
.then(user => {
user.username = req.body.username;

user.save()
.then(() => res.json('User updated!'))
.catch(err => res.status(400).json('Error: ' + err));
})
.catch(err => res.status(400).json('Error: ' + err));
});

Frontend

Imports

Before we start working on the frontend, ensure that you have import the following:

  • Bootstrap
  • React Router
1
2
$ npm install bootstrap
$ npm install react-router-dom

and including this line in App.js:

1
2
import “bootstrap/dist/css/bootstrap.min.css”;
import { BrowserRouter as Router, Route } from “react-router-dom”;

Router Setup

After installing React Router, to get router to work, nest the react App‘s return response in Router tags, as done in the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import { BrowserRouter as Router, Route } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";

function App() {
return (
<Router>
<div className="container">
Hello World
</div>
</Router>
);
}

export default App;

Inside the Router tag, different components can be nested – as is the architecture in React. But to nest components that you wish to keep route dependent, the following example of the App component can be followed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
return (
<Router>
<div className="container">
<Navbar />
<br />
<Route path="/" exact component={ExercisesList} />
<Route path="/edit/:id" component={EditExercise} />
<Route path="/create" component={CreateExercise} />
<Route path="/user" component={CreateUser} />
</div>
</Router>
);
}
export default App;

What’s happening here?
Inside the Router tags, we have listed 5 component. Them being:

  • Navbar
  • ExerciseList
  • EditExercise
  • CreateExercise
  • CreateUser

All of these components would be imported from other files, but the Navbar component would be persistent on screen, where as the other components – which are created using the Route tag, would be rendered conditionally, on the basis on which path the user is accessing. For example, _app_domain_/user would render CreateUser and so on.

Connecting to Router from Elements

Now, how do you render these components selectively from the app? One can do so, using the Link component of the react-router-dom:

1
<Link to="/user" className="nav-link">Create User</Link>

This would create a hyperlink Create User that redirects to /user. To use Link, it needs to be imported from react-router-dom:

1
import { Link } from 'react-router-dom';

Creating Components

Different react components can be created as per need for your application. Here in this section, I would explain the architecture of the following component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import React, { Component } from 'react';

export default class CreateUser extends Component {

constructor(props) {
super(props);

this.onChangeUsername = this.onChangeUsername.bind(this);
this.onSubmit = this.onSubmit.bind(this);

this.state = {
username: ''
};
}

onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
const newUser = {
username: this.state.username,
};
console.log(newUser);

this.setState({
username: ''
})
}

render() {
return (
<div>
<h3>Create New User</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Username: </label>
<input type="text"
required
className="form-control"
value={this.state.username}
onChange={this.onChangeUsername}
/>
</div>
<div className="form-group">
<input type="submit" value="Create User" className="btn btn-primary" />
</div>
</form>
</div>
)
}
}

This component is a stateful component, as in, it has it’s own state defined in the constructor. The defined methods are used to respond to events in the form which is rendered through the render function. Defined methods are bound to the class by including the following statement for each method in the class’ constructor. The character e passed to each method, is the element from the form, which contains attributes from the form element for usage.

1
this.methodName = this.methodName.bind(this);

At this point, the frontend and the backend are separately functional. We’ll now connect the frontend to the backend.

Connecting Frontend to Backend

We do this through the axios package. In your react app, install the package using:

1
$ npm install axios

Import axios to the components where CRUD operations are being performed:

1
import axios from 'axios';

Then use the following snippet to create POST requests from your React app – this is for creating new users.

1
2
axios.post('http://localhost:5000/users/add', newUser)
.then(res => console.log(res.data));

newUser is a JSON object of values that the backend would expect, here, just the username. Do this for all CRUD operations and a the backend would be perfectly integrated with the frontend.

A sample GET request – this would add all exercises to state.

1
2
3
4
5
6
7
axios.get('http://localhost:5000/exercises/')
.then(response => {
this.setState({ exercises: response.data });
})
.catch((error) => {
console.log(error);
})

A sample DELETE request – this would delete exercises by id:

1
2
axios.delete('http://localhost:5000/exercises/'+id)
.then(res => console.log(res.data));

Extra Stuff

React component need not always be created as classes. If all that needs to be done is acceptance of props to return JSX, resort to a functional declaration:

1
2
3
4
5
6
7
8
9
10
11
const Exercise = props => (
<tr>
<td>{props.exercise.username}</td>
<td>{props.exercise.description}</td>
<td>{props.exercise.duration}</td>
<td>{props.exercise.date.substring(0, 10)}</td>
<td>
<Link to={"/edit/" + props.exercise._id}>edit</Link> | <a href="#" onClick={() => { props.deleteExercise(props.exercise._id) }}>delete</a>
</td>
</tr>
)

And that’s it! The information here should be sufficient for you to create a fully functioning MERN stack application. I suggest planning your own pilot app and implementing it with the help of the architecture shown here. If you need any help, feel free to contact me.

Author

Vivek Kaushal

Posted on

2020-04-11

Updated on

2020-10-20

Licensed under

Comments