MERN Stack Tutorial
01-Running express js
Preface
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It is an open-source framework developed and maintained by the Node.js foundation. It is designed for building web applications and APIs. It is the standard server framework for Node.js.
Before starting with express js, ensure that you have node.js, npm and a code editor installed on your system. The installation of node.js and npm can be verified by running the following commands in the terminal.
In my environment I’m using node version v20.11.1
and npm version 10.2.4
on a wsl environment.
You must also ensure that a .gitignore
file is created in the root of the project directory. This file would contain the following lines:
|
|
This would prevent the node_modules
directory from being pushed to the remote repository.
Installing npm packages
To start a new project with express js, first create a new directory named expressjs
and navigate to it. Run the following command within it.
|
|
This would create a package.json
file with default values. Next, install the express
and nodemon
packages by running the following command.
|
|
Here, express
is the package that would be used to create the server and nodemon
is a package that would be used to restart the server automatically when changes are made to the code.
Changing the package.json
file
A couple of changes would be made to the package.json
file. The type
field would be set to module
and a new script would be added to the scripts
field. The scripts
field would look like this:
Coding
Create a new file named index.js
and add the following code to it.
The PORT
variable is imported from a file named config.js
which would be created next. The config.js
file would look like this:
|
|
Running the server
To start the server, run the following command in the terminal.
|
|
This would start the server on port 5000
. To verify that the server is running, open a web browser and navigate to http://localhost:5000
. You should see a message that says Cannot GET /
.
Epilogue
If you want to follow this tutorial section by section please refer to the git commit history.
02-HTTP request
HTTP request methods
- GET: Retrieve data from the server
- POST: Send data to the server
- PUT: Update data on the server
- DELETE: Remove data from the server
- PATCH: Update data partially on the server
- HEAD: Retrieve headers from the server
- OPTIONS: Retrieve supported methods from the server
HTTP response code
- 200: OK
- 201: Created
- 204: No Content
- 400: Bad Request
- 401: Unauthorized
- 403: Forbidden
- 404: Not Found
- 405: Method Not Allowed
- 500: Internal Server Error
- 503: Service Unavailable
Sending an HTTP request using express
Open the previously created index.js
file and add the following code:
The total code will be as follows:
In the above code, we have created a simple server that listens on port 3000 and sends a response with status code 234 and a message “Hello from server” when a request is made to the root URL.
Now to verify this you can run npm run dev
and open the link given in the terminal. You will see the message “Hello from server” on the browser. To verify the status code, you can open the developer tools and check the network tab.
Another method to verify the status code is to use postman. You can install postman from here. Postman is a tool that allows you to send HTTP requests and view the response. You can use postman to send a GET request to the server and check the status code and response.
03=MongoDB
Introduction
MongoDB is a NoSQL database that stores data in JSON-like documents. It is a popular choice for many modern web applications because it can handle large amounts of data and is scalable. There are two ways to access MongoDB. One way is to install the MongoDB server on your computer and use the command line interface to interact with it. The other way is to use a cloud-based service like MongoDB Atlas. Here we will focus on using MongoDB Atlas.
Prerequisites
Before you start, you will need to have a MongoDB Atlas account. You can sign up for free at https://www.mongodb.com/cloud/atlas. Create a new account and proceed. Be sure to select the free tier option when creating your cluster if yo are planning to use it as a hobby project and don’t wish to pay for the services.
Setting up MongoDB Atlas
Create a strong password and store it in a safe place. You will need it to connect to your database and authenticate with username and password. Name the db cluster and click finish. This will take a couple of minutes to set up. Once it’s done, click on the connect button. Click on drivers and select the latest version of Node.js. Copy the connection string and store it in backend/config.js
file. Note that we will store the password in this file itself so be sure to remove this file from the git repository and add it to the .gitignore
file.
Connecting to MongoDB Atlas
Copy the connection string from the MongoDB Atlas dashboard and paste it in the backend/config.js
file. Replace the password with the password you used to create the cluster. The connection string should look something like this:
|
|
Connecting to MongoDB using Mongoose
Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. It manages relationships between data, provides schema validation, and is used to translate between objects in code and the representation of those objects in MongoDB. To connect to MongoDB using Mongoose, you will need to install the mongoose
package. Run the following command in the terminal:
|
|
Now the index.js
file in the backend
directory should look something like this:
|
|
Hwew we include the mongoose
package and use the connect
method to connect to the MongoDB database. If the connection is successful, we start the server. If the connection fails, we log the error to the console. Now you can run the server using the following command:
If the connection is successful, you should see the following message in the terminal:
04-Book model
To create a new model, we need to create a new file in the backend/models
directory. Let’s create a new file called bookModel.js
and add the following code:
|
|
This code creates a new schema for the book model. The schema defines the structure of the document that will be stored in the database. The title
, author
, and publishYear
fields are required and of type String
, String
, and Number
respectively. The timestamps
option creates two fields, createdAt
and updatedAt
, to store the creation and update times of the document. The Book
model is created using the schema and exported.
05-Saving a book using Mongoose
To save a book to the database, we need to create a new route in index.js file in the backend
directory. Let’s create a new route called /books
and add the following code:
|
|
Here we have added a new route /books
that listens for POST requests. When a POST request is received, we check if the required fields are present in the request body. If they are, we create a new book using the Book
model and send a response back to the client. If the required fields are not present, we send a 400 status code with a message asking the client to fill all required fields. If an error occurs, we log the error to the console and send a 500 status code with the error message to the client.
Note that we have added app.use(express.json());
to parse the request body as JSON. This is required to access the request body in the POST request.
Now you can run the server using the following command:
This code cannot be tested via the browser and should be tested via a REST client like Postman. You can use the following code to test the route:
Postman set up
URL: http://localhost:5000/books
Method: POST
Body: raw
Headers: Content-Type: application/json
\
If the request is successful, you should see the following message in the terminal:
And this output in the postman shell (might differ in your case):
06-Getting books from the database
In the previous module we have used the post method to add books to the database. In this module we will use the get method to retrieve books from the database. In order to d othis we can create a new get
route for the /books
endpoint.
This method can further be modified for a more detailed search by modifying the json output.
Similarly to the previous module the endpoints are to be tested using Postman. But instead of using the post method, we will use the get method.
The final output (from postman) should look like this:
07-Getting books by id
What if we want to retrieve a specific book from the database? We can do this by creating a new get
route for the /books/:id
endpoint.
Here the :id
is a route parameter which is used to retrieve the book with the given id, this id is then used to query the database and retrieve the book.
In my case the id was 65ef564e810c88d123eea864
. So the argument in postman must be using the get method and the url must be http://localhost:3000/books/65ef564e810c88d123eea864
. The final output (from postman) should look like this:
08-Updating books
Use of the put
method
The put
method is used to update a book in the database. In order to do this we can create a new put
route for the /books/:id
endpoint.
|
|
This is a route handler for a PUT request in an Express.js application. It’s designed to update a book record in a database. Here’s a step-by-step explanation:
app.put('/books/:id', async (req, res) => {...}
: This line sets up a route for PUT requests to ‘/books/:id’. The ‘:id’ is a route parameter that will be replaced with the ID of the book to update.Inside the route handler, it first checks if the required fields (
title
,author
,publishYear
) are provided in the request body. If not, it sends a 400 status code (Bad Request) with a message.const { id } = req.params;
: This line extracts the ‘id’ from the request parameters.const result = await Book.findByIdAndUpdate(id, req.body);
: This line uses thefindByIdAndUpdate
method of theBook
model to update the book with the given ID. It uses the data from the request body to update the book.If the
findByIdAndUpdate
method does not find a book with the given ID, it returnsnull
. The code checks for this and, if the book is not found, sends a 404 status code (Not Found) with a message.If the book is found and updated, it sends a 200 status code (OK) with a success message.
If any error occurs during this process, it’s caught in the
catch
block, logged to the console, and a 500 status code (Internal Server Error) is sent with the error message.
Similar to the previous modules, the endpoints are to be tested using Postman. But instead of using the post
method, we will use the put
method.
Use the following postman settings
- Method:
put
- URL:
http://localhost:3000/books/<bookid>
- Body:
The final output (from postman) should look like this:
This can be verified by using the get
method and the url must be http://localhost:5000/books/<bookid>
. The final output (from postman) should look like this:
09-Deleting books from the database
In the previous modules, we have used the post
method to add books to the database, the get
method to retrieve books from the database, and the put
method to update books in the database. In this module, we will use the delete
method to remove books from the database.
This is the following route to handle the delete
method:
|
|
The code is self-explanatory. It first checks if the book with the given ID exists in the database. If it does, it deletes the book and sends a 200 status code (OK) with a success message. If the book is not found, it sends a 404 status code (Not Found) with a message. If any error occurs during this process, it’s caught in the catch
block, logged to the console, and a 500 status code (Internal Server Error) is sent with the error message.
Similar to the previous modules, the endpoints are to be tested using Postman. But instead of using the post
method, we will use the delete
method.
First we shall add a new book to the database using the post
method. Then we will use the get
method to retrieve the book’s id. Finally, we will use the delete
method to remove the book from the database.
Adding a new book
- Method:
post
- URL:
http://localhost:5000/books
- Body:
Retrieving the book’s id
- Method:
get
- URL:
http://localhost:5000/books
- The final output (from postman) should look like this:Here the second book has the id
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
{ "count": 2, "data": [ { "_id": "65ef564e810c88d123eea864", "title": "newname", "author": "newauthname", "publishYear": 2023, "createdAt": "2024-03-11T19:06:55.000Z", "updatedAt": "2024-03-14T06:46:16.522Z", "__v": 0 }, { "_id": "65f2a166b3f07128b54a362f", "title": "deletethisbook", "author": "deletethisauthor", "publishYear": 2008, "createdAt": "2024-03-14T07:04:06.831Z", "updatedAt": "2024-03-14T07:04:06.831Z", "__v": 0 } ] }
65f2a166b3f07128b54a362f
and this is the id we will use to delete the book.
Deleting the book
- Method:
delete
- URL:
http://localhost:5000/books/65f2a166b3f07128b54a362f
- The final output (from postman) should look like this:
This can be verified by using the get
method and the url must be http://localhost:5000/books
. The final output (from postman) should look like this:
10-Express router
Having all the routes in the same file can be a bit messy, it makes the code readability harder and debugging a bit more difficult. To solve this problem, Express provides a way to separate the routes into different files using the express.Router
class.
In order to do this we can create a new file called /routes/bookRoutes.js
and move the routes from app.js
to this new file.
|
|
For reference the structure of the index.js
file should look like this BEFORE the refactoring:
|
|
And here is the code after refactoring:
|
|
Note the changes we have done to the index.js
file:
We have removed all the /books
routes from the index.js
file and replaced them with app.use('/books', booksRouter);
. This line tells Express to use the booksRouter
for all the routes that start with /books
.
And the bookRoutes.js
file is a new file that we have created to hold all the /books
routes. We have moved all the /books
routes from the index.js
file to this new file. We have renamed the app
object to router
and we have exported it at the end of the file and refactored the routes to start with /
instead of /books
. This is because we have included this line app.use('/books', booksRouter);
in the index.js
file, so all the routes in the bookRoutes.js
file will be prefixed with /books
.
11-CORS policy
What is CORS?
CORS stands for Cross-Origin Resource Sharing. It is a security feature implemented in web browsers to prevent unauthorized access to resources on a different origin. An origin is defined as the combination of protocol, domain, and port. For example, the origin of https://example.com:8080
is https://example.com:8080
. It is a safety measure to prevent unauthorized access to resources on a different origin. However, there are times when you want to allow access to resources from a different origin. This is where CORS comes in.
Including CORS in your Express app
To use CORS in your Express app, you need to install the cors
package. You can do this by running the following command in your terminal:
|
|
After installing the cors
package, you can include it in your Express app by adding the following line of code to your index.js
file:
|
|
Then, you can use the cors
middleware in your app by adding the following line of code to your index.js
file. This can be done in 2 ways:
- Allow all origins
- Allow specific origins
Note that only one of the above lines should be used in your index.js
file. The first line allows all origins, while the second line allows only the specified origin. You can also specify the methods and headers that are allowed.
12-React setup
What is React?
React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and companies. React can be used as a base in the development of single-page or mobile applications.
How to install React
There are two (main) ways to install React:
- Using
create-react-app
: Advantages: It is the easiest way to get started with React. Disadvantages: It is not as flexible as setting up React from scratch. - Using
vite
: Advantages: It is faster thancreate-react-app
. Disadvantages: It is not as beginner-friendly ascreate-react-app
.
We shall use vite to set up our React app. To create a vite app, run the following command in the root directory of your terminal:
|
|
If vite is not installed, a prompt will appear asking you to install it. Press y
and hit Enter
to install vite. After installing vite, you will be prompted to enter the name of your project. Enter the name of your project and hit Enter
. Vite will now ask the framework you want to use. Select react
and hit Enter
. It will now ask if you want to use TypeScript or JavaScript. We shall use JavaScript, so hit Enter
. Vite will now create a new directory with the name of your project and set up a new React app for you.
Now go into the directory of your project by running the following command in your terminal:
Using Tailwind
Tailwind CSS is a utility-first CSS framework for rapidly building custom designs. It is a low-level framework that provides you with all the building blocks you need to build your own designs. To use Tailwind in your React app, you need to install the tailwindcss
package. You can do this by running the following command in your terminal. But first visit the Tailwind website or dirctly visit the quickstart guide for vite.
Installation
After installing the tailwindcss
package, you can replace it in your React app by adding the following line of code to your tailwind.config.js
file:
Now you can replace the code of index.css
with the following code:
Delete App.css
for now
In the App.jsx
file, replace the code with the following:
To make development easier, you use the ES7 React/Redux/GraphQL/React-Native snippets extension in VSCode. This extension provides you with a lot of snippets that you can use to write your code faster. Using rafce
snippet will create a functional component with an export statement.
To run this code in your browser, run the following command in your terminal:
|
|
Unlike the modules before you have to run the command in the frontend
directory. This will start a development server and open your app in your default browser. The output will look like this:
13-React router dom
The react router dom is a package that allows you to use the routing in your react application. It is a collection of navigational components that compose declaratively with your application.
Installation
|
|
Usage
Go to the main.jsx
file and import the BrowserRouter
and Route
components from the react-router-dom
package.
You have to create a new folder within the src
folder called page
. This folder will contain the components that will be used in the routes. Create 5 different files within the page
folder.
Home.jsx
ShowBook.jsx
EditBook.jsx
CreateBook.jsx
DeleteBook.jsx
Now you have to modify the App.jsx
file to use the Route
component.
|
|
14-Showing books in react
In this part we need both the frontend and the backend of the project and we will be using the backend from the previous part.
Packages needed
First cd into the frontend folder and run the following command to install the packages needed.
Axios is a promise based HTTP client for the browser and node.js and react-icons is a library that provides popular icons for your react project.
Tweaking the backend
Open the /backend/index.js
and do the following changes.
We are commenting out the cors options because we are not going to use it in this part.
Creating react components for loading state
Create a folder in frontend/src
called components
and create a file called Spinner.jsx
and add the following code.
This is a simple spinner component that we will use to show the user that the books are being loaded.
Now we have to create a component to show the books
Modifying frontend/src/page/Home.jsx
to show the books.
|
|
Explanation of the code:
This is a React component named Home
that fetches and displays a list of books from a backend server. Here’s a breakdown of what each part of the code does:
Imports: The necessary modules and components are imported. This includes React itself, the
useState
anduseEffect
hooks from React, theaxios
library for making HTTP requests, aSpinner
component (presumably a loading spinner), several icons fromreact-icons
, and theLink
component fromreact-router-dom
.State Variables: Two state variables are declared using the
useState
hook:books
(an array to store the list of books) andloading
(a boolean to track whether the data is currently being fetched).Data Fetching: The
useEffect
hook is used to fetch the list of books from the backend server when the component is first rendered. Theaxios.get
function is used to make a GET request to the server. If the request is successful, the response data is stored in thebooks
state variable andloading
is set tofalse
. If an error occurs, it’s logged to the console andloading
is also set tofalse
.Rendering: The component returns a JSX element that displays a list of books. If the data is still being fetched (
loading
istrue
), aSpinner
component is displayed. Otherwise, a table is displayed with each book’s details. Each book has links to its detail, edit, and delete pages, which are created using theLink
component fromreact-router-dom
.Export: Finally, the
Home
component is exported for use in other parts of the application.
Modifying frontend/src/App.js
to show the Home
component.
|
|
Running the code
To run this code two instances of the terminal must be made. One for the backend and the other for the frontend. In the backend terminal run the following command.
In the frontend terminal run the following command.
Open the link provided by the frontend page and you will see the books being displayed.
Preview of the page:
References
- freecodecamp.org
- My github repo derived from the above link