Hello guys,
The question I intend to answer with this tutorial is; how do I develop a MERN Stack app? So, by the end of this tutorial you will be able to develop MERN app in CRUD(create, Read, Update and Delete) form.
I will assume you have some basic understanding of MERN stack as one of the most relevant app development stack in the world which is growing fast every day, use by many developers around the globe with a huge community support.
Simply put, a MERN stack is a combination of four technologies: Mongo DB, Express JS, React JS, and Node JS. More importantly, MERN Stack is combination of open source database and Javascript library & runtime environment. Each letter of this acronym means;
- Mongo DB: A document-based open-source database, that provides scalability and flexibility.
- Express JS: A structured base designed to develop web applications and APIs.
- React JS: A Javascript Front-end library for building user interfaces. Maintained by Facebook.
- Node JS: A javascript runtime environment built on Chrome’s V8 JS engine.
Alternatively, you might come across another acronym very similar to MERN, called MEAN, there is, it’s almost the same, but the difference is that we use Angular instead of React.
Note that this tutorial is the first part of MERN stack beginner’s series(part 2 - How to Dockerize MERN stack app in AWS EC2 & part 3 - How to Deploy MERN stack app on AWS ECS, it is advised to follow this sequentially. Also, find the links to the remaining part at the end this tutorial). So, I’m going to teach you how to develop a simple MERN stack Application.
I believe you will agree with me that, the best way to learn anything is by doing it. So, I will guide you into developing this simple app using MERN stack, here we’re going to create a book rating web app called bukrate in CRUD (Create, Read, Update and Delete) form. For proper understanding, I will break this tutorial into four sections; Requirements, Setting up server side, Setting up client side and Integrating server & client side
REQUIREMENTS;
Basic requirement includes;
a)basic javascript, HTML, CSS
b)code editor; preferably VS Code
c)setup development environment on your laptop/desktop
d)install browser, preferably google chrome
e)install nodejs
f)setup MongoDB Atlas
1.0 SETTING UP SERVER SIDE
In this section, we’re going to create the server of our application, where we’re going to create a RESTful API following the steps below.
Firstly, we create an empty directory that will be the root of our system.
$ mkdir bukrate-app
$ cd bukrate-app
The above directory represent folder for our project - bukrate app. Inside this bukrate-app, let’s now create another empty folder called server that will be our backend folder.
$ mkdir server
$ cd server
Follow by creating our package.json inside this server folder.
So, what’s a package.json and how can we create one?
package.json file is the heart of Node. js system. It is the manifest file of any Node. js project and contains the metadata of the project which is required before publishing to NPM, and also defines functional attributes of a project that npm uses to install dependencies, run scripts, and identify the entry point to our package.
To create a package.json you need a Package Manager, preferably choose NPM (Node Package Manager) which we will be using in this tutorial. Feel free to use what you prefer say alternatively YARN
Now we can create our file using NPM.
$ npm init -y
While creating this file, you’ll be asked questions related to your project. If you’d like to keep the default provided by NPM, you can just type enter until the end.
After that, you can find the file in the server folder. Then we'll able to install our dependencies. Check out the list and uses of our dependencies;
- Express: It’s the server framework (The E in MERN).
- Body Parser: Responsible to get the body off of network requests.
- Nodemon: Restart the server when it sees changes (for a better dev experience).
- Cors: Package for providing a Connect/Express middleware that can be used to enable CORS with various options.
- Mongoose: It’s an elegant MongoDB object modeling for node.js
Then, install all the dependencies by running this command;
$ npm install express body-parser cors mongoose nodemon
Immediately, after successfully create these dependencies, you will found additional two new folder node_modules and package-lock.json. With these two files, your project can be interpreted.
From here on, we can now create NodeJS file(at the root of server folder) called index.js which contain all the logic for our API/server as follow.
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const mongoose = require('mongoose')
const app = express()
const apiPort = 3000
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())
app.use(bodyParser.json())
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`))
To start the application you just need to run the following command:
node index.js
You should see the message “Server running on port 3000” it means that everything you’ve done until now it’s correct.
Then open a browser and type localhost:3000, you’ll see the message “Hello World”.
The good news is, you’re able to see your server running. Next is to set up database for our application.
1.1 Setup Cloud MongoDB
We need to either install MongoDB on the computer locally or use the cloud database. For this project we use the cloud - Atlas;
This is simple, you can just follow the step provided by MongoDB documentation here. MongoDB
In an attempt not to make this tutorial unnecessarily long, I will not spend much time on setting up the database, but will try to highlight each step, also this tutorial can be of great help Link;
a)sign up Here
b)choose Atlas
c)build cluster
d)named the cluster if choose to
e)build database; - by clicking on the collection. - Add My Own Data. - Add Database and Collection name
f)Setup Database Access; - to add user
g)connect to the app database; - connect to your application, - add method.
With this, we’ve just created our database with these steps.
Now, let's return back to our javascript code, we need to create a connection from our server using the Mongoose library.
Now, let's update the main Nodejs file server/index.js we have something like this.
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const mongoose = require('mongoose')
const app = express()
//connect to mongodb
const dbURI = 'mongodb+srv://<username>:<password>@bukrate-app.lfqmj.mongodb.net/bukrate?retryWrites=true&w=majority';
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => app.listen(PORT, () => console.log(`Server Running on Port: http://localhost:${PORT}`)))
.catch((error) => console.log(`${error} did not connect`));
const apiPort = 3000
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())
app.use(bodyParser.json())
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`))
You should notice I have added like five line after the comment //connect to mongodb
Because of this update, let's restart the application to see the effect, then you need to close and open a new one. This is where nodemon will start its working, it’s going to restart the server whenever it sees changes in any file of our project. So, let’s do this by running the command.
$ nodemon index.js
1.1.1. Creating Book’s Schema
We need to create an entity called book that should be composed of the name of a book. For this, we just need to know the name of the book and the author of the book, and to add an important information, the rating.
Let’s go ahead and create a folder called models and add a file called rate.js.
$ mkdir models
$ cd models
$ touch rate.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const rateSchema = new Schema(
{
name: { type: String, required: true },
author: { type: String, required: true },
rating: { type: Number, required: true },
},
{ timestamps: true },
)
const Rate = mongoose.model('Rate', rateSchema);
module.exports = Rate;
1.2. Getting and Saving Data
Here, we’ll create all the CRUD operations and create our REST endpoints. Let’s create two more folders inside the server: routes and controllers. In the route folder, let’s create the file rate-router.js and in the controller folder, rate-ctrl.js.
$ mkdir routes controllers
$ touch routes/rate-router.js
$ touch controllers/rate-ctrl.js
const Rate = require('../models/rate')
createRate = (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a rate',
})
}
const rate = new Rate(body)
if (!rate) {
return res.status(400).json({ success: false, error: err })
}
rate
.save()
.then(() => {
return res.status(201).json({
success: true,
id: rate._id,
message: 'rate created!',
})
})
.catch(error => {
return res.status(400).json({
error,
message: 'Rate not created!',
})
})
}
updateRate = async (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a body to update',
})
}
Rate.findOne({ _id: req.params.id }, (err, rate) => {
if (err) {
return res.status(404).json({
err,
message: 'Rate not found!',
})
}
rate.name = body.name
rate.author = body.time
rate.rating = body.rating
rate
.save()
.then(() => {
return res.status(200).json({
success: true,
id: rate._id,
message: 'Rate updated!',
})
})
.catch(error => {
return res.status(404).json({
error,
message: 'Rate not updated!',
})
})
})
}
deleteRate = async (req, res) => {
await Rate.findOneAndDelete({ _id: req.params.id }, (err, rate) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!rate) {
return res
.status(404)
.json({ success: false, error: `Rate not found` })
}
return res.status(200).json({ success: true, data: rate })
}).catch(err => console.log(err))
}
getRateById = async (req, res) => {
await Rate.findOne({ _id: req.params.id }, (err, rate) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!rate) {
return res
.status(404)
.json({ success: false, error: `Rate not found` })
}
return res.status(200).json({ success: true, data: rate })
}).catch(err => console.log(err))
}
getRates = async (req, res) => {
await Rate.find({}, (err, rates) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!rates.length) {
return res
.status(404)
.json({ success: false, error: `Rate not found` })
}
return res.status(200).json({ success: true, data: rates })
}).catch(err => console.log(err))
}
module.exports = {
createRate,
updateRate,
deleteRate,
getRates,
getRateById,
}
const express = require('express')
const RateCtrl = require('../controllers/rate-ctrl')
const router = express.Router()
router.post('/rate', RateCtrl.createRate)
router.put('/rate/:id', RateCtrl.updateRate)
router.delete('/rate/:id', RateCtrl.deleteRate)
router.get('/rate/:id', RateCtrl.getRateById)
router.get('/rates', RateCtrl.getRates)
module.exports = router
Lastly, let’s add the router in our server/index.js file.
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const mongoose = require('mongoose')
const Rate = require('./models/rate')
const app = express()
//connect to mongodb
const dbURI = 'mongodb+srv://<username>:<password>@bukrate-app.lfqmj.mongodb.net/bukrate?retryWrites=true&w=majority';
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => app.listen(PORT, () => console.log(`Server Running on Port: http://localhost:${PORT}`)))
.catch((error) => console.log(`${error} did not connect`));
const rateRouter = require('./routes/rate-router')
const apiPort = 3000
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())
app.use(bodyParser.json())
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.use('/api', rateRouter)
app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`))
1.2.1 Protecting Your Database
Up till here, there shouldn't be any issue with our application. But if you want to test the application you can use either Postman or Robo 3T. These tools help us in verify our application workings (You can find how to from the documentation on their websites or other resources on google).
Remember how you have to input your database access's username and password during the MongoDB setup. So, imagine you have a course to share your source code to the public, say on GitHub, you need to protect access to your database.
Firstly, we need another dependencies to run;
$ npm install dotenv
Then, create .env file server/.env
Inside index.js, add and update with another variables;
const dotenv = require('dotenv')
Also cut the MongoDB link from line 12 and replace with
process.env.MONGO_URL
Then inside .env file;
MONGO_URL = mongodb+srv://<username>:<password>@bukrate-app.lfqmj.mongodb.net/bukrate?retryWrites=true&w=majority
Congratulation! We’ve just finished the server side. With all these knowledge you’re now in position to create other entities, try to create a user entity, maybe an employee or a table price, use your imagination is your limitation.
Let’s go straight up to setting up client side.
2.0 SETTING UP CLIENT SIDE
This is where we’re going to create all the user-interface parts, where the user will interact with our application.
Firstly, we’re going to the root of our project and create the client-side. For this, we need to understand about NPX.
NPX is a tool that its goal is to help round out the experience of using packages from the NPM registry. As NPM makes it super easy to install and manage dependencies hosted on the registry, NPX makes it easy to use CLI tools and other executables hosted on the registry.
Let’s create our directory. So, the server-side will be for the Backend and we have the client-side for the Frontend.
$ npx create-react-app client
$ cd client
$ npm start
The application should opened this screen if you get it right.
You will remember that the default port is 3000, but we’ve already set this port to our Backend. Then, let’s changes it to 7000. Meanwhile, you choose whatever number.
To change the port we need to change it on client/package.json. Include PORT= in the file.
...
},
"scripts": {
"start": "set PORT=7000 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
...
You will discover that React creates some default files. Let’s remove some of the unnecessaries files for us;
$ rm App.css index.css App.test.js serviceWorker.js
To view the complete app, we need to set up our project by installing some other dependencies like we do in the server side. These are what we need; Axios, Bootstrap, Styled-Components, and React Table.
- axios: It’s a promise-based the asynchronous code. It’s the most popular promise based HTTP.
- bootstrap: It’s is an open-source toolkit and the most popular front-end component library where allows you for developing with HTML, CSS, and JS.
- styled-components: It allows you to write actual CSS code to style your components.
- react-table: It’s a lightweight, fast, and extendable data grid built for React.
- react-router-dom: DOM bindings for React Routers.
$ npm install styled-components react-table react-router-dom axios bootstrap --save
In the src directory, we should create the new directories that will be the structure of our project. Create an index.js file inside each directory, except the app folder.
$ cd src
$ mkdir api app components pages style
$ touch api/index.js components/index.js pages/index.js style/index.js
Move the App.js file to the app directory, but renaming to index.js.
Now is time for us to start the coding,
First of all, update our file client/src/index.js for the following code.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
ReactDOM.render(<App />, document.getElementById('root'))
After this, we’ll develop the header of the application. And create the other components of our project. Create the new files NavBar.jsx, Logo.jsx, and Links.jsx.
$ touch components/NavBar.jsx components/Logo.jsx components/Links.jsx
Hey! What is JSX extension?
Simple, JSX is a notation that Reacts chose to identify a JavaScript’s eXtension. It’s recommended to use it with React to describe what the UI should look like.
Now, let’s create our files.
For the Links;
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import styled from 'styled-components'
const Collapse = styled.div.attrs({
className: 'collpase navbar-collapse',
})``
const List = styled.div.attrs({
className: 'navbar-nav mr-auto',
})``
const Item = styled.div.attrs({
className: 'collpase navbar-collapse',
})``
class Links extends Component {
render() {
return (
<React.Fragment>
<Link to="/" className="navbar-brand">
MERN Application
</Link>
<Collapse>
<List>
<Item>
<Link to="/rates/list" className="nav-link">
List Book Rates
</Link>
</Item>
<Item>
<Link to="/rates/create" className="nav-link">
Create Book Rate
</Link>
</Item>
</List>
</Collapse>
</React.Fragment>
)
}
}
export default Links
For the Logo;
import React, { Component } from 'react'
import styled from 'styled-components'
import logo from '../logo.svg'
const Wrapper = styled.a.attrs({
className: 'navbar-brand',
})``
class Logo extends Component {
render() {
return (
<Wrapper href="https://deoluoyinlola.netlify.app">
<img src={logo} width="50" height="50" alt="deoluoyinlola.netlify.app" />
</Wrapper>
)
}
}
export default Logo
For the NavBar;
import React, { Component } from 'react'
import styled from 'styled-components'
import Logo from './Logo'
import Links from './Links'
const Container = styled.div.attrs({
className: 'container',
})``
const Nav = styled.nav.attrs({
className: 'navbar navbar-expand-lg navbar-dark bg-dark',
})`
margin-bottom: 20 px;
`
class NavBar extends Component {
render() {
return (
<Container>
<Nav>
<Logo />
<Links />
</Nav>
</Container>
)
}
}
export default NavBar
The components/index.js file will help to export our components, it will allow us to import them in other files using the notation; import { blabla } from './components'
import Links from './Links'
import Logo from './Logo'
import NavBar from './NavBar'
export { Links, Logo, NavBar }
Lastly, let's update app/index.js. Then, we able to see the development of the project.
import React from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import { NavBar } from '../components'
import 'bootstrap/dist/css/bootstrap.min.css'
function App() {
return (
<Router>
<NavBar />
</Router>
)
}
export default App
3.0 INTEGRATING SERVER AND CLIENT SIDE
Now let's learn how to integrate the server with our client.
First of all, let’s update the file api/index.js.
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000/api',
})
export const insertRate = payload => api.post(`/rate`, payload)
export const getAllRates = () => api.get(`/rates`)
export const updateRateById = (id, payload) => api.put(`/rate/${id}`, payload)
export const deleteRateById = id => api.delete(`/rate/${id}`)
export const getRateById = id => api.get(`/rate/${id}`)
const apis = {
insertRate,
getAllRates,
updateRateById,
deleteRateById,
getRateById,
}
export default apis
Amazing! Now we can develop our routes. For this, we’ll need to update the file app/index.js adding the routes.
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import { NavBar } from '../components'
import { RatesList, RatesInsert, RatesUpdate } from '../pages'
import 'bootstrap/dist/css/bootstrap.min.css'
function App() {
return (
<Router>
<NavBar />
<Switch>
<Route path="/rates/list" exact component={RatesList} />
<Route path="/rates/create" exact component={RatesInsert} />
<Route
path="/rates/update/:id"
exact
component={RatesUpdate}
/>
</Switch>
</Router>
)
}
export default App
On pages folder, let’s create the files that will do the role of each application’s page: RatesList.jsx, RatesInsert.jsx and RatesUpdate.jsx.
$ cd pages
$ touch RatesList.jsx RatesInsert.jsx RatesUpdate.jsx
For now, let’s create simples files where you can see the page’s transition when you click in each link of the NavBar.
For RateList;
import React, { Component } from 'react'
class RatesList extends Component {
render() {
return (
<div>
<p>In this page you'll see the list of rates</p>
</div>
)
}
}
export default RatesList
For RateInsert;
import React, { Component } from 'react'
class RatesInsert extends Component {
render() {
return (
<div>
<p>In this page you'll see the form to add a book</p>
</div>
)
}
}
export default RatesInsert
For RateUpdate;
import React, { Component } from 'react'
class RatesUpdate extends Component {
render() {
return (
<div>
<p>In this page you'll see the form to update the books</p>
</div>
)
}
}
export default RatesUpdate
For index.js
import RatesList from './RatesList'
import RatesInsert from './RatesInsert'
import RatesUpdate from './RatesUpdate'
export { RatesList, RatesInsert, RatesUpdate }
OK, let’s edit our file to get the ratings from the database: RatesList.jsx.
import React, { Component } from 'react'
import ReactTable from 'react-table'
import api from '../api'
import styled from 'styled-components'
import 'react-table/react-table.css'
const Wrapper = styled.div`
padding: 0 40px 40px 40px;
`
class RatesList extends Component {
constructor(props) {
super(props)
this.state = {
rates: [],
columns: [],
isLoading: false,
}
}
componentDidMount = async () => {
this.setState({ isLoading: true })
await api.getAllRates().then(rates => {
this.setState({
rates: rates.data.data,
isLoading: false,
})
})
}
render() {
const { rates, isLoading } = this.state
console.log('TCL: RatesList -> render -> rates', rates)
const columns = [
{
Header: 'ID',
accessor: '_id',
filterable: true,
},
{
Header: 'Name',
accessor: 'name',
filterable: true,
},
{
Header: 'Author',
accessor: 'author',
filterable: true,
},
{
Header: 'Rating',
accessor: 'rating',
filterable: true,
},
{
Header: 'Time',
accessor: 'time',
Cell: props => <span>{props.value.join(' / ')}</span>,
},
]
let showTable = true
if (!rates.length) {
showTable = false
}
return (
<Wrapper>
{showTable && (
<ReactTable
data={rates}
columns={columns}
loading={isLoading}
defaultPageSize={10}
showPageSizeOptions={true}
minRows={0}
/>
)}
</Wrapper>
)
}
}
export default RatesList
Awesome, in this list let’s include two more things, these things will be buttons. One button to delete another button to update.
import React, { Component } from 'react'
import ReactTable from "react-table-6"
import api from '../api'
import styled from 'styled-components'
import "react-table-6/react-table.css"
const Wrapper = styled.div`
padding: 0 40px 40px 40px;
`
const Update = styled.div`
color: #ef9b0f;
cursor: pointer;
`
const Delete = styled.div`
color: #ff0000;
cursor: pointer;
`
class UpdateRate extends Component {
updateUser = event => {
event.preventDefault()
window.location.href = `/rates/update/${this.props.id}`
}
render() {
return <Update onClick={this.updateUser}>Update</Update>
}
}
class DeleteRate extends Component {
deleteUser = event => {
event.preventDefault()
if (
window.confirm(
`Do you want to delete the movie ${this.props.id} permanently?`,
)
) {
api.deleteRateById(this.props.id)
window.location.reload()
}
}
render() {
return <Delete onClick={this.deleteUser}>Delete</Delete>
}
}
class RatesList extends Component {
constructor(props) {
super(props)
this.state = {
rates: [],
columns: [],
isLoading: false,
}
}
componentDidMount = async () => {
this.setState({ isLoading: true })
await api.getAllRates().then(rates => {
this.setState({
rates: rates.data.data,
isLoading: false,
})
})
}
render() {
const { rates, isLoading } = this.state
console.log('TCL: RatesList -> render -> rates', rates)
const columns = [
{
Header: 'ID',
accessor: '_id',
filterable: true,
},
{
Header: 'Name',
accessor: 'name',
filterable: true,
},
{
Header: 'Author',
accessor: 'author',
filterable: true,
},
{
Header: 'Rating',
accessor: 'rating',
filterable: true,
},
{
Header: 'Time',
accessor: 'time',
Cell: props => <span>{props.value.join(' / ')}</span>,
},
{
Header: '',
accessor: '',
Cell: function(props) {
return (
<span>
<DeleteRate id={props.original._id} />
</span>
)
},
},
{
Header: '',
accessor: '',
Cell: function(props) {
return (
<span>
<UpdateRate id={props.original._id} />
</span>
)
},
},
]
let showTable = true
if (!rates.length) {
showTable = false
}
return (
<Wrapper>
{showTable && (
<ReactTable
data={rates}
columns={columns}
loading={isLoading}
defaultPageSize={10}
showPageSizeOptions={true}
minRows={0}
/>
)}
</Wrapper>
)
}
}
export default RatesList
As you can see, now we’re able to delete a book rate, but we still not able to update it, because we haven’t finished the component yet.
Before, to develop the update page, let’s create the insertion page.
import React, { Component } from 'react'
import api from '../api'
import styled from 'styled-components'
const Title = styled.h1.attrs({
className: 'h1',
})``
const Wrapper = styled.div.attrs({
className: 'form-group',
})`
margin: 0 30px;
`
const Label = styled.label`
margin: 5px;
`
const InputText = styled.input.attrs({
className: 'form-control',
})`
margin: 5px;
`
const Button = styled.button.attrs({
className: `btn btn-primary`,
})`
margin: 15px 15px 15px 5px;
`
const CancelButton = styled.a.attrs({
className: `btn btn-danger`,
})`
margin: 15px 15px 15px 5px;
`
class RatesInsert extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
author: '',
rating: '',
time: '',
}
}
handleChangeInputName = async event => {
const name = event.target.value
this.setState({ name })
}
handleChangeInputAuthor = async event => {
const author = event.target.value
this.setState({ author })
}
handleChangeInputRating = async event => {
const rating = event.target.validity.valid
? event.target.value
: this.state.rating
this.setState({ rating })
}
handleChangeInputTime = async event => {
const time = event.target.value
this.setState({ time })
}
handleIncludeRate = async () => {
const { name, rating, time } = this.state
const arrayTime = time.split('/')
const payload = { name, rating, time: arrayTime }
await api.insertRate(payload).then(res => {
window.alert(`Book inserted successfully`)
this.setState({
name: '',
author: '',
rating: '',
time: '',
})
})
}
render() {
const { name, author, rating, time } = this.state
return (
<Wrapper>
<Title>Create Book</Title>
<Label>Name: </Label>
<InputText
type="text"
value={name}
onChange={this.handleChangeInputName}
/>
<Label>Author: </Label>
<InputText
type="text"
value={author}
onChange={this.handleChangeInputAuthor}
/>
<Label>Rating: </Label>
<InputText
type="number"
step="0.1"
lang="en-US"
min="0"
max="10"
pattern="[0-9]+([,\.][0-9]+)?"
value={rating}
onChange={this.handleChangeInputRating}
/>
<Label>Time: </Label>
<InputText
type="text"
value={time}
onChange={this.handleChangeInputTime}
/>
<Button onClick={this.handleIncludeMovie}>Add Book</Button>
<CancelButton href={'/rates/list'}>Cancel</CancelButton>
</Wrapper>
)
}
}
export default RatesInsert
And finally, let’s create the update file.
Friend, we have come to the end of this tutorial. In this article, you have seen the structure to build a MERN application using diverse libraries provided by an amazing community around the world.
In this article, my intuit was to keep things simple for your understanding. This is a project that you can do many enhancements.
Maybe you can try, for instance, create a better structure to include the year of the book or you can improve the code to put the update and insertion book in the same file, what about to create a new entity called the customer and create the same RESTful operation for it? You can add a lot of new features to this. If you prefer, tell me in the comments.
You can find the complete github repo Here
Also, is advised to follow up this tutorial up with the next part - How Dockerize MERN App on AWS EC2, Link here
I hope that I have contributed to your knowledge. Feel free to tell me what I should improve on to write better articles.
I love to see you in the next part.
Top comments (0)