Crud Application using Mern Stack
Hello everyone! Today I’m here to demonstrate how you can use the Mern stack to build a very simple Crud application!
If you’re unsure what MERN is, please check this blog.
N.B Even though the best way to learn this is by coding along with the instructions but still I’d suggest you to download my code, if you are not able to follow along with everything. This is mainly because my way of explaining things might seem incoherent to many. And also, once you open up and see the files for yourself it might make a bit more sense.
Now let’s dive right in!
Step 1:
First things first, let’s create our root folder. I called mine “POKEMONCRUDMERN” and then lets create two directories inside of it called client and backend.

After that let’s open up our terminal on Vscode, I’m using git bash as my terminal. I’ll recommend doing this so I’m suggesting this link which shows you how you can do the same.
After opening up the terminal I’m going to install some dependencies. I’ll go over each dependency as we encounter it’s usage but let’s install them first.
First we need to create or initialize our package.json file.
Inorder to do that we can type the commands :
npm init
OR
npm init -y
The first one will want you to input some basic information and the second one will initialize the package.json without requiring you to provide all that information.

After that let’s install the following things:

I’ll go over each of them as we stumble upon their use in our code.
Step 2:

In server.js type in the above code.
Now let’s analyze what’s going on.
- In line 1, we are including the module “express” by using the function require(). We installed this module in step 2 along with some other modules. So when we import the “express” module we basically get returned a function. Now let’s store that function in a constant variable called “express”.
- Now let’s invoke the function “express” and then store that result in a constant variable called “app”. This app variable is actually an object. So in short, whenever this express function gets called we get an object and in our case we stored that object in a variable called “app”. Now we can use different functions of this object called app like app.use(), app.get(), app.listen()etc, more of which we will see later on.
- At Line 3 we stored the result from importing the module “cors”. I’ll mention why we need this later on.
- At Line 4, 5 we are importing the “dotenv” module and by calling the config() function we are basically making sure that we can pull or extract all our existing environment variables from our .env file.
- At Line 8, we created a variable called Port, which will be equal to the environment variable called port if it has been initialized
OR
It will be set to the port 5000. You can set it to other ports if you want.
- Now let’s use the app.listen() function to listen to the PORT 5000. And then let’s show the Line “Server running on port 5000” on our console.

That being done, our basic server has been created.

Now let’s place these two lines in the above way in our code.
The use() function is a way to register or use middleware or chain of middlewares before executing intermediary route logic depending upon order of middleware registration sequence.
Here cors and express.json are our middlewares.
CORS is a node.js package for providing an Express middleware that can be used to enable Cross-Origin Resource Sharing. Cross-origin requests are made using HTTP request methods. So inorder to enable these methods we need to enable CORS.
Express.json() allows us to send json objects in the body of our request during various http requests like POST,PUT etc.
Step 3: Connecting our Database
Follow this link to learn how you can configure the settings for mongoDb atlas and how you can use mongoDb compass. Now create a new database called “pokemons” and a collection called “pokemonData”.
Now we’re gonna create our schema and our model.
- Create a .env dile and then place your copied mongodb uri for connecting to mongoDb via compass into that .env file. We won’t be keeping our .env file because of security issues.
- In server.js, we are going to establish a connection to mongoDb using this url from the .env file.

Explanation:
Here we are basically making a connection to our database using mongoose. mongoose.connect() takes in our mongo_uri from our .env file which we brought in earlier and then the second parameter takes in an object of some required parameters to support the connection to the newest version of mongoDb.
By doing all this we would to able to establish a connection, which will output the aforementioned line in the console only when a proper connection is established.

- Create a Folder called “models” inside the backend folder and then create a file called “PokemonData.js”.
- In “PokemonData.js” we are gonna create our schema in the following way:

Explanation:
Here we are just creating a schema by specifying our fields and the constraints for our fields too. Then we are registering or compiling a model for our schema by calling mongoose.model() on our schema. Then we’ll export it and then later on it will be imported in our server.js file right above our db connection.

Step 4: Designing our frontend:
Our frontend folder structure will look something like this:

Let’s now run the command “npm i react-router-dom” inside the client folder. This will enable us to create appropriate routes, links in our database.
Then let’s run the command “npm i axios”. Axios is a promise based http client. It can help us make HTTP requests to fetch or save data from the client side.
I’ve provided a minimalistic styling by providing the “bootstrap.min.css” so that you don’t have to worry too much about styling.
Let’s go over our App.js first,
import AddPokemon from './components/AddPokemon';import EditPokemon from './components/EditPokemon';import PokemonTable from './components/PokemonTable';import Navbar from './layouts/Navbar';import Axios from 'axios';import { BrowserRouter as Router, Route, Link } from 'react-router-dom';import './App.css';function App() {function addNewPokemon(Name, Type) {Axios.post('http://localhost:5000/insert', {Name: Name,Type: Type,});alert('New Pokemon Added!');}return (<Router><div className="App"><Navbar></Navbar><Routeexactpath="/"component={() => (<AddPokemon addNewPokemon={addNewPokemon}></AddPokemon>)}></Route><Route exact path="/PokemonTable" component={PokemonTable}></Route><Routepath="/EditPokemon/:id/:name/:type"component={EditPokemon}></Route></div></Router>);}export default App;
Explanation:
Here we have created our routes for our components and have also passed in necessary functions and things as props by those components, to be used by child components.
In the addNewPokemonFunction(),we are sending a request to the server to input out values received by the AddPokemon.js component. Then those values get passed to the server and an insert operation is done.
We have also created routes for showing the EditPokemon View and the PokemonTable view.
In this code snippet,
<Routepath="/EditPokemon/:id/:name/:type"component={EditPokemon}></Route>
The “:id” portion of that route specifies that whatever you put into that position will be passed as the id in the params map.
The “:name” and “:type” portion of that route specifies that whatever you put into that position will be passed as the name and type in the params map.
So whenever a client side passes in the id, name, type in the EditPokemon.js file, the server can receive these values and perform functions accordingly.
In Navbar.js,
import React, { Component } from 'react';import { Link } from 'react-router-dom';export default class navbar extends Component {render() {return (<div><nav class="navbar navbar-expand-lg navbar-dark bg-primary"><a class="navbar-brand" href="#">POKEMON CRUD</a><buttonclass="navbar-toggler"type="button"data-toggle="collapse"data-target="#navbarColor01"aria-controls="navbarColor01"aria-expanded="false"aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarColor01"><ul class="navbar-nav mr-auto"><li class="nav-item active"><Link to="/"><a class="nav-link" href="#">Home<span class="sr-only">(current)</span></a></Link></li><Link to="/PokemonTable"><li class="nav-item"><a class="nav-link" href="#">Pokemon List</a></li></Link></ul></div></nav></div>);}}
The output will look be something like this:

Explanation:
In Navbar.js we basically are creating a navigation bar to move to different routes within our website. The routes and links are created using react-router-dom. This makes it ridiculously easy and fast to navigate to different pages without reloading the pages.
Let’s go over the files of our component folder first. These 3 files will contain the views to add a new pokemon, edit an existing pokemon and then lastly to show the pokemon list in the form of a table.
In AddPokemon.js,
import React, { Component } from 'react';export default class AddPokemon extends Component {constructor(props) {super(props);this.state = {Name: '',Type: '',};}render() {return (<div><p>Add New Pokemons!</p><div class="form-group"><label>{' '}Name:<inputonChange={(e) => {this.setState({ Name: e.target.value });}}type="text"name="Name"placeholder="Enter Pokemon Name"></input></label></div><div class="form-group"><label>Type:<selectonChange={(e) => {this.setState({ Type: e.target.value });}}name="type"><option value="grass">Grass</option><option value="electric">Electric</option><option value="water">Water</option><option value="Fire">Fire</option></select></label></div><buttonclass="btn btn-primary"onClick={() => {this.props.addNewPokemon(this.state.Name, this.state.Type);}}>SUBMIT</button></div>);}}
This will give us something like this:

Explanation:
So basically what we’re doing is that we’re creating our input and select fields for the Name, Type of our pokemon. And after that, we’ve created a state to hold the updated values of those fields. And whenever the user hits submit, the function “addNewPokemon” is going to be invoked and the Name, Type state values are going to be passed in. This addNewPokemon() function is actually a function prop passed in by the parent element and so the parent element can catch these values.
In EditPokemon.js,
import Axios from 'axios';import React, { useState } from 'react';
function EditPokemon(props) {const name = props.match.params.name;const type = props.match.params.type;const [Name, setName] = useState(name);const [Type, setType] = useState(type);
function editPokemon() {const id = props.match.params.id;alert('Changes updated!');Axios.put('http://localhost:5000/edit/', {id: id,Name: Name,Type: Type,});}return (<div><div class="form-group my-3"><label>{' '}Name:<inputvalue={Name}onChange={(e) => {setName(e.target.value);}}type="text"name="Name"placeholder="Enter Pokemon Name"></input></label></div><div class="form-group"><label>Type:<selectvalue={Type}onChange={(e) => {setType(e.target.value);}}name="type"><option value="grass">Grass</option><option value="electric">Electric</option><option value="water">Water</option><option value="Fire">Fire</option></select></label></div><buttonclass="btn btn-warning"onClick={() => {editPokemon(Name, Type);}}>UPDATE</button></div>);}export default EditPokemon;
The output of this will be:

Explanation:
The look and feel of this is exactly like that of AddPokemon.js but the key difference is that here the form is for updating a single pokemon entry. We are using useState hook in this case for handling the changes to the Pokemon Name or Pokemon Type.
Here firstly we are catching the id, name, type values that are passed as parameters. These things are actually the old values of our pokemon try which will populate the input and select fields when they are first loaded and they will be set in the name, type variable of the the two useState hooks.
After that, whenever we type into the input field or select a new option, the value of the state also changes. Then finally when we hit the “Update” button, these new values will be sent in the body of a particular request and will be sent to the server, where the server will handle the request by function designed to handle POST requests for that route.
In PokemonTable.js,
import React, { useEffect, useState } from 'react';import { Link } from 'react-router-dom';import Axios from 'axios';function PokemonTable() {const [list, setList] = useState([]);useEffect(() => {Axios.get('http://localhost:5000/show').then((res) => {setList(res.data);});}, [list]);function deletePokemon(id) {alert("ITEM DELETED");Axios.delete(`http://localhost:5000/delete/${id}`);}return (<div><table class="table table-hover"><thead><tr><th scope="col">Type</th><th scope="col">Name</th><th scope="col">Type</th><th scope="col">EDIT</th><th scope="col">DELETE</th></tr></thead><tbody>{list.map((x) => (<tr class="table-active"><th scope="row">Active</th><td>{x.Name}</td><td>{x.Type}</td><td><Link to={`/EditPokemon/${x._id}/${x.Name}/${x.Type}`}><button class="btn btn-warning">EDIT</button>{' '}</Link></td><td><buttononClick={() => {deletePokemon(x._id);}}class="btn btn-danger">DELETE</button></td></tr>))}</tbody></table></div>);}export default PokemonTable;
The output will be :

Explanation:
This table just shows the Name, Type of all the pokemons and an edit, delete option following each of them. The useEffect hook retrieves data from the address ‘http://localhost:5000/show'.
We also have a useState hook which keeps the Pokemon List, and a method for updating the Pokemon List. Therefore, the data retrieved earlier in the useEffect hook is used by the method called “setList” and stored in the “list” variable”. This list is later on iterated and then shown in the form of a table. Whenever we hit the “Delete” button after each entry,
Step 5: Defining our routes:
In server.js before listening to the port 5000, let’s paste in the following code snippets.
app.get('/show', async (req, res) => {await PokemonModel.find({}, (err, result) => {if (!err) res.send(result);});});app.post('/insert', async (req, res) => {const Name = req.body.Name;const Type = req.body.Type;const newPokemon = new PokemonModel({Name: Name,Type: Type,});await newPokemon.save();});app.put('/edit', async (req, res) => {const Name = req.body.Name;const Type = req.body.Type;try {await PokemonModel.findById(req.body.id, (err, newPokemon) => {newPokemon.Name = Name;newPokemon.Type = Type;newPokemon.save();});} catch (err) {console.log(err);}});app.delete('/delete/:id', async (req, res) => {await PokemonModel.findByIdAndRemove(req.params.id).exec();});
Explanation:
Here we are defining how our server will react to the http requests of adding ,showing, editing, deleting data by using separate functions for handling POST,GET,PUT,DELETE requests respectively. Here all the requests coming from our client side views are handled using these functions.
In all the functions, we are specifying the route first and then a callback function which takes in a request and response object.
In app.get(), we are retrieving all the entries from our model via the find method and then if there’s no error we are sending it as a response.
In app.post(), we are instantiating a new element with the passed in values from the client side, and then saving it into our database. The values are found as properties of the req.body object.
In app.put(), we are editing a particular entry by first perfoming a lookup using its id. And then we do the actual updation to the particular entry using the passed in new values from the client side.The values are found as properties of the req.body object.
In app.delete(), we are deleting the particular id the client side wants us to delete.
The link to the entire code will be found in this link.
I also will have other blogs regarding mern in the future so stay tuned for them if you liked this one!