MERN Stack CRUD App: React, Node, Express & MongoDB

by Jhon Lennon 52 views

Hey guys! Today, we're diving deep into the world of MERN stack development by building a CRUD (Create, Read, Update, Delete) application. If you're new to the MERN stack, it's a powerful combination of technologies that includes MongoDB, Express.js, React.js, and Node.js. This stack is perfect for building modern, scalable, and dynamic web applications. In this article, we will build a simple but functional CRUD app using these technologies. This comprehensive guide will walk you through each step, providing you with a solid foundation to build upon.

The MERN stack offers several advantages. Firstly, it uses JavaScript across the entire stack, which simplifies development and allows for code reuse. Secondly, React's component-based architecture makes it easy to manage the user interface, while Node.js provides a robust backend environment. Finally, MongoDB's flexible schema-less database is ideal for handling diverse data requirements. Together, these technologies create a synergistic environment for rapid and efficient web development.

Setting Up the Development Environment

Before we start coding, we need to set up our development environment. First, make sure you have Node.js and npm (Node Package Manager) installed on your system. You can download them from the official Node.js website. After installing Node.js, verify the installation by running node -v and npm -v in your terminal. This will confirm that Node.js and npm are correctly installed and accessible.

Next, install MongoDB on your local machine. You can download the appropriate version for your operating system from the MongoDB website. Follow the installation instructions provided on the website to set up MongoDB correctly. Once installed, ensure that the MongoDB server is running. You can typically start the MongoDB server by running the mongod command in your terminal. Keep this server running in the background as we develop our application.

Finally, create a new directory for your project and navigate into it using the terminal. Initialize a new Node.js project by running npm init -y. This command creates a package.json file, which will manage our project dependencies. With our environment set up, we are ready to start building the backend with Node.js and Express.

Building the Backend with Node.js and Express

Let's start by creating the backend for our CRUD application using Node.js and Express. First, install Express.js and Mongoose, an elegant MongoDB object modeling tool, by running npm install express mongoose body-parser cors. These packages will help us create the server and interact with our MongoDB database.

Create a file named server.js in your project directory. This file will contain the code for our Express server. Require the necessary modules, including Express, Mongoose, body-parser, and cors, at the top of the file. Body-parser is used to parse incoming request bodies, while cors enables Cross-Origin Resource Sharing, allowing our React frontend to communicate with the backend.

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const port = 5000;

app.use(cors());
app.use(bodyParser.json());

Next, connect to the MongoDB database using Mongoose. Replace 'mongodb://localhost:27017/mern_crud' with your MongoDB connection string. Define a Mongoose schema for your data model. For example, if you are managing tasks, your schema might include fields like title and description.

mongoose.connect('mongodb://localhost:27017/mern_crud', { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('Connected to MongoDB'))
  .catch(err => console.error('Could not connect to MongoDB', err));

const taskSchema = new mongoose.Schema({
  title: String,
  description: String
});

const Task = mongoose.model('Task', taskSchema);

Implement the CRUD operations by defining API endpoints for creating, reading, updating, and deleting tasks. Use Express route handlers to handle these requests. For example, the following code snippet demonstrates how to create a new task:

app.post('/tasks', async (req, res) => {
  try {
    const task = new Task(req.body);
    const savedTask = await task.save();
    res.status(201).json(savedTask);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
});

Similarly, implement the other CRUD operations: GET for reading, PUT for updating, and DELETE for deleting tasks. Ensure that each endpoint handles errors gracefully and returns appropriate HTTP status codes. Finally, start the server and listen for incoming requests.

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

Building the Frontend with React

Now, let’s build the frontend of our CRUD application using React. First, create a new React application using Create React App by running npx create-react-app client. This command sets up a new React project with all the necessary dependencies and configurations.

Navigate into the client directory and start the development server by running npm start. This will open the React application in your browser. Now, let’s create the components for our application. Create a components directory inside the src directory and add the following components:

  • TaskList.js: Displays a list of tasks.
  • TaskForm.js: Allows users to create and update tasks.
  • TaskItem.js: Represents a single task item.

In TaskList.js, fetch the list of tasks from the backend API and display them. Use the useEffect hook to fetch the data when the component mounts. Map over the tasks array and render each task using the TaskItem component.

import React, { useState, useEffect } from 'react';
import TaskItem from './TaskItem';

function TaskList() {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    const fetchTasks = async () => {
      const response = await fetch('http://localhost:5000/tasks');
      const data = await response.json();
      setTasks(data);
    };

    fetchTasks();
  }, []);

  return (
    <ul>
      {tasks.map(task => (
        <TaskItem key={task._id} task={task} />
      ))}
    </ul>
  );
}

export default TaskList;

In TaskForm.js, create a form that allows users to enter the title and description of a task. Use the useState hook to manage the form inputs. When the form is submitted, send a POST request to the backend API to create a new task.

import React, { useState } from 'react';

function TaskForm() {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();

    const newTask = {
      title: title,
      description: description
    };

    await fetch('http://localhost:5000/tasks', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(newTask)
    });

    setTitle('');
    setDescription('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Title" />
      <textarea value={description} onChange={(e) => setDescription(e.target.value)} placeholder="Description"></textarea>
      <button type="submit">Add Task</button>
    </form>
  );
}

export default TaskForm;

In TaskItem.js, display the details of a single task and provide buttons for updating and deleting the task. Implement the update and delete functionality by sending PUT and DELETE requests to the backend API.

Connecting Frontend to Backend

To connect the React frontend to the Node.js backend, you need to make API calls from the React components to the backend endpoints. Use the fetch API or a library like Axios to make these calls. Ensure that you handle the responses correctly and update the UI accordingly.

For example, to fetch the list of tasks from the backend, you can use the following code in your TaskList component:

useEffect(() => {
  const fetchTasks = async () => {
    const response = await fetch('http://localhost:5000/tasks');
    const data = await response.json();
    setTasks(data);
  };

  fetchTasks();
}, []);

Similarly, to create a new task, you can use the following code in your TaskForm component:

const handleSubmit = async (e) => {
  e.preventDefault();

  const newTask = {
    title: title,
    description: description
  };

  await fetch('http://localhost:5000/tasks', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(newTask)
  });

  setTitle('');
  setDescription('');
};

Make sure to handle errors gracefully and update the UI to reflect the changes made to the data. For example, after creating a new task, you can update the task list by fetching the updated list from the backend.

Conclusion

Alright, awesome work! You've successfully built a CRUD application using the MERN stack! This project provides a solid foundation for building more complex web applications. You can extend this application by adding features like user authentication, authorization, and real-time updates. Keep practicing and exploring the MERN stack to become a proficient full-stack developer. Happy coding!