MERN Full Stack Application (Part 1/4 CRUD)-MongoDB,Express.js, Node.js (Backend) and React.js (Frontend)- Newsletter Subscription Example 3-Tier Application (Create Data)
Application Preview:

This is the first example application in the 4 part series covering details on CRUD.We know that CRUD is an acronym for create,read,update and delete.
C — This example application only posts data
i.e creates data.
This simple MERN 3-tier example application allows users to subscribe to a Newsletter. Here, React.js helps create the user interface (Tier-1- Client) and Express.js provides the node.js framework for the web server (Tier-2- Server) alongside the cloud hosted MongoDB Database (Tier-3- Database). Bootstrap provides the frontend toolkit, Axios library delivers a promise based HTTP client for the browser and node.js, Nodemon detects changes to the working directory and automatically restarts the node application, Mongoose assists the object data modeling (ODM) for MongoDB and node.js, body-parser middleware supports node.js body parsing, cors enables the Cross-Origin Resource Sharing(CORS) and the dotenv loads environment variables from a .env file.
Commençons (let’s Start):
Open the Visual Studio code editor and the integrated terminal.
Create a newsletter directory and cd into it
Now let’s start creating the app frontend, using the react’s build setup.
npx create-react-app client
cd client
npm install react-bootstrap bootstrap axios
npm start
This will create a client directory, install all essential basic dependencies and start the development server on port 3000
Access it using http://localhost:3000/

You can terminate the server using CTRL+ C
In the terminal use
code .
to access the files
Before we code:
→ Delete all files (except app.js and index.js) from src folder.
- Open the index.js and edit the file to end up with the following code:
import React from “react”;
import ReactDOM from “react-dom”;
import App from “./App”;ReactDOM.render(<App />, document.getElementById(“root”));
2.Open the app.js and edit the file to end up with the following code:
function App() {
return (
<div>
<h1>NewsLetter</h1>
</div>
);
}export default App;
→ Delete all files (except index.html) from public folder.
3.Open the index.html and edit the file to end up with the following code:
<!DOCTYPE html>
<html lang=”en”>
<head>
<title>Newsletter</title>
</head>
<body>
<div id=”root”></div>
</body>
</html>
Now, use npm start and we have the following on our localhost:3000

Let’s Code:
create a new file styles.css in the src folder and add the code for styles targeting the tags and classes.
html,
body {
height: 100%;
}body {
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
}p {
color: red;
}
.success {
color: darkgreen;
text-align: center;
}
create a new file Home.js in the src folder and add the following code to import CSS styles,bootstrap,react and axios.
import “bootstrap/dist/css/bootstrap.min.css”;
import “./styles.css”;
import Container from “react-bootstrap/Container”;
import Form from “react-bootstrap/Form”;
import Button from “react-bootstrap/Button”;
import { useState, useEffect } from “react”;
import axios from “axios”;
now append it with the following code:
function Home() {
// States to handle data,errors and form submission.
const [data, setData] = useState({ uname: “”, email: “” });
const [dataerr, setDataerr] = useState({});
const [isSubmit, setIsSubmit] = useState(false);// function to handle changes in user input
function changeHandler(event) {
setData((prev) => {
const { name, value } = event.target;
return {
…prev,
[name]: value,
};
});
}// Perform a post request to the server listening on port 5000(local host)
useEffect(() => {
console.log(dataerr);
if (Object.keys(dataerr).length === 0 && isSubmit) {
axios
.post(“http://localhost:5000/posts", data)
.then((response) => console.log(response.data));
setData({ uname: “”, email: “” });
}
}, [dataerr]);// function to handle data submission after validation
function submitHandler(event) {
setDataerr(validate(data));
setIsSubmit(true);
event.preventDefault();
}// function to validate user input and return errors (if any)
function validate(values) {
console.log(values);
const errors = {};
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
if (!values.uname) {
errors.uname = “Name is required!”;
}
if (!values.email) {
errors.email = “Email is required!”;
} else if (!regex.test(values.email)) {
errors.email = “Not valid !,Valid Format = name@example.com”;
}
return errors;
}

// Form to gather user input
return (
<div>
<h1 className=”h3 mb-4 fw-normal”> Subscribe to the Newsletter </h1>
<Container>
<Form>
<Form.Group>
<Form.Control
name=”uname”
value={data.uname}
onChange={changeHandler}
type=”text”
placeholder=”Enter name”
required
/>
</Form.Group>
<p>{dataerr.uname}</p>
<Form.Group>
<Form.Control
name=”email”
value={data.email}
onChange={changeHandler}
type=”email”
placeholder=”name@example.com”
required
/>
</Form.Group>
<p>{dataerr.email}</p>
<br></br>
<Button onClick={submitHandler} variant=”primary btn-lg w-100">
Submit
</Button>
</Form>
</Container>
<br />
{Object.keys(dataerr).length === 0 && isSubmit ? (
<div className=”success”>Subscribed successfully</div>
) : (
<p></p>
)}
</div>
);
}export default Home;
import Home from “./Home”;
and replace <h1>NewsLetter</h1> with <Home /> in app.js
Now, the frontend is ready but we cannot submit a post request to the server yet. So, let’s work on setting up the backend.
lets work on Tier -2 & Tier -3:
Create a server directory and cd into it
initiate a new npm package with
npm init -y
add 6 npm packages with
npm express mongoose nodemon body-parser cors dotenv
now they are available to use as shown under dependencies in package.json

also,add nodemon to the scripts to automatically reflect changes when index.js is updated.

Create a index.js and add the following code to import express, body Parser,cors, dotenv,mongoose and the routes.
import express from “express”;
import bodyParser from “body-parser”;
import cors from “cors”;
import dotenv from “dotenv”;
import postRoutes from “./routes.js”;
import mongoose from “mongoose”;
Now append the code to use the top-level function exported by the express module and app.use to mount middleware functions.
const app = express();
app.use(bodyParser.json({ limit: “30mb”, extended: true }));
app.use(bodyParser.urlencoded({ limit: “30mb”, extended: true }));
app.use(cors());app.use(“/posts”, postRoutes);
append with the following code to specify the port ,configure dotenv to load environment variables from a .env file and connect to the Database CONNECTION_URL specified in .env file using mongoose.
const PORT = process.env.PORT || 5000;
dotenv.config()
mongoose
.connect(process.env.CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() =>
app.listen(PORT, () => console.log(`server running on port: ${PORT}`))
)
.catch((error) => console.log(error.message));
Follow the steps to create a mongoDB Atlas cluster
once done get the connection url
create a .env file in the server folder with the CONNECTION_URL(Sample connection url shown below,replace it with your cluster url)
CONNECTION_URL= “mongodb+srv://<username>:<password>@clustername.mongodb.net/test?retryWrites=true&w=majority&useNewUrlParser=true&useUnifiedTopology=true”;
Now define the routes and the schema to setup the backend
create routes.js file in server directory and add the code to create routes
import express from “express”;
import {createPost } from “./controllers.js”;//create new router object and post route
const router = express.Router();
router.post(“/”,createPost);
export default router;
create controllers.js file in server directory using the follwing code:
import PostMessage from ‘./schema.js’
export const createPost = async(req, res) => {
const post= req.body;const newPost= new PostMessage(post);
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({message :error.message});
}
req.body gathers data from client by parsing and then uses the schema to post data to the Database.
create schema.js to be used by controllers.js.The schema is created using mongoose.
import mongoose from “mongoose”;
const postSchema = mongoose.Schema({
uname: String,
email: String,
});const PostMessage = mongoose.model(“PostMessage”, postSchema);
export default PostMessage;
Now the backend is ready and can be tested using Rest Client extension for visual studio.
now start the server on local host 5000 using nodemon using:
npm start

once server is running on port 5000. create a test.http file and add the following code:
POST http://localhost:5000/posts
Content-Type: application/json{
“uname”:”cxgvfgb”,
“email”:”vfcxgb@mail.com”
}

Click on send request within the test.http to json data.
Browse the collections tab of your cluster to view the data.

Check for database and network access if you have problems with posting data to the Database.
Now that we are able to submit data using the test setup, let’s submit a post request from the client (tier-1) to the server (tier-2) to push data to the database(tier-3).
cd into client folder (while keeping the server running) and
npm start

we will not be able to submit the form until the fields are valid

once we have a valid input, data gets posted and we have a successfully subscribed message.

Browse mongoDB collections to see both the posts from the test setup and the other with user input.

We have successfully pushed data to the database from the client via the server.
R -In the next blog let’s work towards reading the data that has already been created by this application.
References:
https://reactjs.org/docs/getting-started.html
https://expressjs.com/en/starter/hello-world.html
https://getbootstrap.com/docs/5.1/getting-started/introduction/
https://docs.atlas.mongodb.com/
https://axios-http.com/docs/intro
https://github.com/remy/nodemon#nodemon
https://mongoosejs.com/docs/guide.html
https://www.npmjs.com/package/body-parser
https://www.npmjs.com/package/cors
https://www.npmjs.com/package/dotenv