How to Set Up MERN Stack: Easy Step-by-Step Guide

How to Set Up MERN Stack: Easy Step-by-Step Guide

Introduction

The MERN stack is a powerful combination of technologies for building robust, full-stack applications. It comprises MongoDB, Express.js, React.js, and Node.js. This guide will introduce you to the basics of each technology and demonstrate how they work together to create a complete application.

Crafting the Perfect Project Structure for MERN

First, let’s set up the project structure. Open the terminal and create a root folder. Then, add separate folders for the client and server.

mkdir mern-app
cd mern-app
mkdir client
mkdir server
  • your-app-name/

    • client/

      • node_modules/

      • public/

        • favicon.ico
      • src/

        • assets/

          • images/
        • components/

        • common/

        • pages/

        • services/

        • styles/

          • index.css
        • utils/

        • App.jsx

        • index.jsx

        • main.jsx

      • index.html

      • .gitignore

      • package.json

      • README.md

      • vite.config.js

    • server/

      • node_modules/

      • controllers/

      • models/

      • routes/

      • middlewares/

      • config/

        • db.js
      • utils/

      • index.js

    • .env

    • .gitignore

    • package.json

    • README.md

Mastering MongoDB with Mongoose: A Step-by-Step Guide

MongoDB is a NoSQL database known for its high performance, high availability, and easy scalability. Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. It manages relationships between data, provides schema validation, and translates objects in code to their representations in MongoDB. Mongoose is a MongoDB object modeling tool designed to work in an asynchronous

Getting Started with MongoDB and Mongoose environment. It provides a straight-forward, schema-based solution to model your application data. Mongoose supports both promises and callbacks for writing queries.

  1. Now, navigate to the server folder and initialize a new Node.js project:

     cd server
     npm init -y
    
  2. Setting Up Mongoose for MongoDB in Your MERN Stack Application

     npm install mongoose
    
  3. Establishing a MongoDB Connection with Mongoose in Your MERN Stack Application

    Create a new db.js file in the server folder. This file will contain the Mongoose code needed to establish a connection to your MongoDB database. First, require the Mongoose library at the top of the file:

     const mongoose = require('mongoose');
    

    Next, define the connection string for your MongoDB database. If you're using a local instance of MongoDB, it might look something like this:

     const dbURI = 'mongodb://localhost:27017/mern-app';
    

    For a production environment or a cloud-based MongoDB service like MongoDB Atlas, your connection-string will be different and should include your credentials and the database-URL.

    Now, use Mongoose to connect to the database. Add the following code to handle the connection and any potential errors:

     mongoose.connect(dbURI)
       .then(() => console.log('MongoDB connected successfully'))
       .catch(err => console.log('MongoDB connection error: ', err));
    

    To ensure that your connection is properly managed, you can also add event listeners for the connection:

     mongoose.connection.on('connected', () => {
       console.log('Mongoose connected to ' + dbURI);
     });
    
     mongoose.connection.on('error', (err) => {
       console.log('Mongoose connection error: ' + err);
     });
    
     mongoose.connection.on('disconnected', () => {
       console.log('Mongoose disconnected');
     });
    

    Finally, to handle the termination of the connection gracefully when the application is closed, you can add the following code:

     process.on('SIGINT', async () => {
       await mongoose.connection.close();
       console.log('Mongoose connection closed due to app termination');
       process.exit(0);
     });
    

    Your db.js file should now look like this:

     const mongoose = require('mongoose');
    
     const dbURI = 'mongodb://localhost:27017/mern-app';
    
     mongoose.connect(dbURI)
       .then(() => console.log('MongoDB connected successfully'))
       .catch(err => console.log('MongoDB connection error: ', err));
    
     mongoose.connection.on('connected', () => {
       console.log('Mongoose connected to ' + dbURI);
     });
    
     mongoose.connection.on('error', (err) => {
       console.log('Mongoose connection error: ' + err);
     });
    
     mongoose.connection.on('disconnected', () => {
       console.log('Mongoose disconnected');
     });
    
     process.on('SIGINT', async () => {
       await mongoose.connection.close();
       console.log('Mongoose connection closed due to app termination');
       process.exit(0);
     });
    

    With this setup, your application is now ready to connect to MongoDB using Mongoose. With this setup in place, your application is now ready to connect to MongoDB using Mongoose. The next step is to start defining schemas and models for your data.

    Schemas in Mongoose are used to define the structure of your documents within a collection. They provide a blueprint for your data, specifying the fields and their types, as well as any validation requirements or default values. For example, you can define a simple schema for a user collection like this:

  4. Initiate the following command in your terminal:

     mkdir schemas
    

    This will create a directory named schemas in your server folder. Now, create a file named UserSchema.js for your schema.

     // server/schemas/UserSchema.js
    
     const mongoose = require('mongoose');
     const Schema = mongoose.Schema;
    
     const userSchema = new Schema({
       name: {
         type: String,
         required: true
       },
       email: {
         type: String,
         required: true,
         unique: true
       },
       password: {
         type: String,
         required: true
       },
       createdAt: {
         type: Date,
         default: Date.now
       }
     });
    
     const User = mongoose.model('User', userSchema);
     module.exports = User;
    

    In this example, the userSchema defines four fields: name, email, password, and createdAt. The name, email, and password fields are required, meaning that a document cannot be saved to the database without these fields. The email field is also unique, ensuring that no two users can have the same email address. The createdAt field has a default value of the current date and time.

    Once you have defined your schema, you can create a model from it using mongoose.model(). The model provides an interface to interact with the database, allowing you to create, read, update, and delete documents. For instance, you can create a new user like this:

     const newUser = new User({
       name: 'User Name',
       email: 'user.name@example.com',
       password: 'securepassword'
     });
    
     newUser.save()
       .then(user => console.log('User created: ', user))
       .catch(err => console.log('Error creating user: ', err));
    

    By defining schemas and models, you can ensure that your data adheres to a consistent structure and take advantage of Mongoose's powerful features for data validation, querying, and more, enabling you to interact with the database in a structured and efficient manner.

Building Robust Backends with Express.js

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

Getting Started with Express.js

  1. Install Express.js and nodemon:

     npm install express
     npm install --save nodemon
    
  2. Create an Express.js App: Use the express() function to create an Express.js application and use require("./db") to import the database file, ensuring it runs when you start your server:

     const express = require('express');
     const app = express();
     const port = process.env.PORT || 3000;
    
     require("./db");
    
  3. Define Routes: To set up routes for your application, you can use the app.get() method for handling GET requests and the app.post() method for handling POST requests. These methods allow you to specify the URL path and a callback function that will be executed when the route is accessed.

    For example, to create a simple route that responds with "Hello World" when accessed, you can use the following code:

     app.get('/', (req, res) => {
       res.send('Hello World');
     });
    

    In this example, when a user navigates to the root URL (/), the server will respond with the text "Hello World".

    Similarly, you can define a POST route to handle form submissions or other data sent to the server. Here is an example of a POST route:

     app.post('/submit', (req, res) => {
       const data = req.body;
       // Process the data here
       res.send('Form submitted successfully');
     });
    

    In this case, when a POST request is made to the /submit URL, the server will process the data received in the request body and respond with a confirmation message.

    By defining routes in this way, you can create a structured and organized way to handle different types of requests in your Express.js application. This makes it easier to manage and scale your application as it grows.

  4. To start your Express.js server and listen on a specified port, use the app.listen method. In this example, the server begins listening on the defined port, and once the server is running, it logs a message to the console indicating that the server is operational and specifying the port number. This ensures that your server is ready to handle incoming requests.

     app.listen(port, () => {
       console.log(`Server is running on port ${port}`);
     });
    

    Your index.js file should now look like this:

     // server/index.js
     const express = require("express");
     const mongoose = require("mongoose");
    
     const app = express();
     const port = process.env.PORT || 3000;
     // importing the db file to connect with database
     require("./db");
    
     // Defining GET request for route '/'
     app.get("/", (req, res) => {
       res.send("Hello World");
     });
    
     // Defining POST request for route '/submit'
     app.post("/submit", (req, res) => {
       const data = req.body;
       // Process the data here
       res.send("Form submitted successfully");
     });
    
     // Listening the server
     app.listen(port, () => {
       console.log(`Server is running on port ${port}`);
     });
    
  5. Next, update your package.json file by replacing the existing code with the following scripts:

      "scripts": {
          "dev": "nodemon index.js"
        },
    
  6. Finally, execute the following command in your terminal to launch your server and bring your application to life:

     npm run dev
    

    Congratulations! Your backend server is now up and running successfully, ready to handle incoming requests and power your application.

Creating Dynamic Frontends with React.js and Vite

React.js is a JavaScript library for building user interfaces. It allows you to create reusable UI components. Vite is a build tool that aims to provide a faster and leaner development experience for modern web projects.

Getting Started with React.js and Vite

Navigate to the client folder and create a new React.js application with Vite:

cd ../client
npx create-vite . --template react
  1. Install Tailwind CSS

    Install tailwindcss and its peer dependencies, then generate your tailwind.config.js and postcss.config.js files.

      npm install -D tailwindcss postcss autoprefixer
      npx tailwindcss init -p
    
  2. Configure your template paths

    Add the paths to all of your template files in your tailwind.config.js file.

      /** @type {import('tailwindcss').Config} */
      export default {
        content: [
          "./index.html",
          "./src/**/*.{js,ts,jsx,tsx}",
        ],
        theme: {
          extend: {},
        },
        plugins: [],
      }
    
  3. Add the Tailwind directives to your CSS

    Add the @tailwind directives for each of Tailwind’s layers to your ./src/index.css file.

      @import 'tailwindcss/base';
      @import 'tailwindcss/components';
      @import 'tailwindcss/utilities';
    
  4. Create a Function Component

    In React, a function component is a simple way to create a component using a JavaScript function. Here’s how to create a basic function component:

      import React from "react";
    
      const App = () => {
        return (
          <h1 className='text-3xl font-bold text-indigo-800 text-center'>
            Hello, world!
          </h1>
        );
      };
    
      export default App;
    

Starting the Frontend Server

To start the frontend server, run the following command in your terminal:

npm run dev

With your frontend server up and running, you can now start building dynamic and interactive user interfaces using React.js and Vite. This setup ensures a smooth development experience, allowing you to focus on creating a responsive and efficient frontend for your MERN stack application.

You can run both the frontend and backend simultaneously by using different terminals, or you can use a package to run both of them concurrently. VS Code offers this feature; you can click the split button to divide the terminals. Then, you can enter the commands for the frontend and backend respectively.

Wrapping Up: Harnessing the Power of the MERN Stack

The MERN stack is a powerful tool for building full-stack applications. By understanding the basics of MongoDB, Express.js, React.js, and Node.js, you can start building your own applications. Remember, practice is key when learning new technologies, so don’t be afraid to start a project and learn as you go! Happy coding!