[React + Node.js]: Create your MERN Stack Application - Part 1: Backend
React
Node.js
03/12/2020
Overview
This post will show you how to create a MERN (MongoDB, Express.js, React, Node.js) Stack Application from the scratch to the end. Part 1 will focus on the server side with REST API. Client side will be covered in the upcoming post, Part 2. This app will involve a basic CRUD (Create, Read, Update, Delete) operations along with simple layout in React.
The full project is available on this repo.
MongoDB
Let's start off with setting the database cluster with MongoDB. If you haven't, go to MongoDB to sign up & sign in. Then, create a free, Starter Clusters
with default settings.
Once your cluster is created, press CONNECT
Add your auto-filled IP address to whitelist and create a DB username & password
Choose a connection method as MongoDB Compass
. Then follow the instructions to install MongoDB Compass
on your system
Copy the string that will be used to connect to database in your back-end and in MongoDB Compass
Once installed, connect to MongoDB Compass
This is where you can visually see & manage data that we haven't created yet. Leave this behind for now and continue to the next step.
Node.js & Express
package.json
mern-demo └── package.json
First, create a folder where you would like to contain your MERN stack application. Here, I'll create a folder mern-demo
. Then, browse over to the created folder and create package.json
with npm init -y
command. Make sure that package.json
was created and run the following commands to install required node packages.
npm i express mongoose concurrently
concurrently
allows to run multiple commands concurrently. It'll be used to run back-end and front-end server at the same time
If you have not yet installed nodemon
, install it as dev dependency.
npm i -D nodemon
nodemon
continuously watches the changes in files so that you wouldn't have to restart the server every time you make changes. You can choose to install it globally withsudo npm i -g nodemon
With those packages installed, modify "scripts"
in package.json
to start developing.
"scripts": {- "test": "echo \"Error: no test specified\" && exit 1"+ "start": "node app.js",+ "server": "nodemon app.js",},
config.js & app.js
mern-demo ├── app.js ├── config.js └── package.json
Create config.js
that will contain your MongoDB URI that you've copied
config.js
module.exports = { MONGODB_URI: `mongodb+srv://<DB_USERNAME>:<PASSWORD>@cluster0-xc4db.mongodb.net/test`,}
Then create app.js
. This is the main server that will listen to the request from the client side
app.js
const express = require("express")const mongoose = require("mongoose")const { MONGODB_URI } = require("./config")
const app = express()app.use(express.json())
// Set CORS headerapp.use((req, res, next) => { res.setHeader("Access-control-Allow-Origin", "*") res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE") // Allow client to set headers with Content-Type res.setHeader("Access-Control-Allow-Headers", "Content-Type") next()})
// Error Handlerapp.use((error, req, res, next) => { const status = error.statusCode || 500 const message = error.message const data = error.data // Passing original error data res.status(status).json({ message: message, data: data })})
// DB connectionmongoose .connect(MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, }) .then(result => { const port = process.env.PORT || 8080 app.listen(port, () => { console.log(`Listening on port ${port}...`) }) }) .catch(err => { // Handle error })
At this point, when you run
npm server
, the server will start listening to port 8080
User Schema
mern-demo ├── models | └── user.js ├── app.js ├── config.js └── package.json
const mongoose = require("mongoose")const Schema = mongoose.Schema
const userSchema = new Schema( { name: { type: String, required: true, }, age: { type: String, required: true, }, occupation: { type: String, required: true, default: "unemployed", }, }, { timestamps: true })
module.exports = mongoose.model("User", userSchema)
Each user will have 3 properties: name, age, occupation
Router & Controller
mern-demo ├── controllers | └── user.js ├── routes | └── user.js ├── models | └── user.js ├── app.js ├── config.js └── package.json
routes/user.js
const express = require("express")const userController = require("../controllers/user")const router = express.Router()
// GET /userrouter.get("/user", userController.getUsers)// POST /userrouter.post("/user", userController.postUser)// DELETE /user/:userIdrouter.delete("/user/:userId", userController.deleteUser)module.exports = router
Router here defines the 3 CR
UD operations
controllers/user.js
const User = require("../models/user")
exports.getUsers = async (req, res, next) => { try { const users = await User.find().sort({ createdAt: -1 }) res.status(200).json({ message: "Fetched users", users: users, }) } catch (err) { if (!err.statusCode) { err.statusCode = 500 } next(err) }}
exports.postUser = async (req, res, next) => { try { let occupation = req.body.occupation if (!occupation) occupation = "unemployed" const user = new User({ name: req.body.name, age: req.body.age, occupation: occupation, }) await user.save() // Save in db
res.status(201).json({ message: "User created", user: user, }) } catch (err) { if (!err.statusCode) { err.statusCode = 500 } next(err) }}
exports.deleteUser = async (req, res, next) => { const userId = req.params.userId try { const user = await User.findById(userId) if (!user) { const error = new Error("Couldn't find the user") error.statusCode = 404 throw error } await User.findByIdAndRemove(userId) res.status(200).json({ message: "User removed" }) } catch (err) { if (!err.statusCode) { err.statusCode = 500 } next(err) }}
Register Created Route
app.js
const express = require("express")const mongoose = require("mongoose")const { MONGODB_URI } = require("./config")const userRoutes = require("./routes/user")
const app = express()app.use(express.json())
// Set CORS headerapp.use((req, res, next) => { res.setHeader("Access-control-Allow-Origin", "*") res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE") // Allow client to set headers with Content-Type res.setHeader("Access-Control-Allow-Headers", "Content-Type") next()})
// Register Routeapp.use(userRoutes)
// Error Handlerapp.use((error, req, res, next) => { const status = error.statusCode || 500 const message = error.message const data = error.data // Passing original error data res.status(status).json({ message: message, data: data })})
// DB connectionmongoose .connect(MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false, }) .then(result => { const port = process.env.PORT || 8080 app.listen(port, () => { console.log(`Listening on port ${port}...`) }) }) .catch(err => { // Handle error })
What's next? 🤔
- [React + Node.js]: Create your MERN Stack Application - Part 2: Frontend
- [Deploy]: Deploy your App on Netlify
- [React + Node.js]: Create your MERN Stack Application - Part 2: Frontend