Setting up a full-stack MERN (MongoDB, Express, React, Node.js) app for deployment on Vercel.
Tags: serverless, node, react, mongodb
Published: 2024-07-14 00:17:00

The MERN stack, comprising MongoDB, Express, React, and Node.js, is one of several technological combinations used for building full-stack web applications. Vercel provides a versatile platform capable of deploying a wide range of frameworks and technologies for various project types. While often associated with frontend hosting, Vercel also supports backend deployments, making it suitable for MERN applications. 

This article will guide you through the process of setting up a MERN stack application and deploying it on Vercel. We'll cover the steps from local development to deployment, showing how to utilize Vercel's free tier for testing your project and, if desired, hosting it long-term. By the end of this guide, you'll understand how to leverage the MERN stack and Vercel's deployment capabilities to bring your web application online and make it accessible to users.


**Setting up the Development Environment**
To beign, you'll need to install the following tools:

  1. Node.js and npm
     - Visit [https://nodejs.org/en/download/package-manager](https://nodejs.org/en/download/package-manager)
     - Download and install the LTS (Long Term Support) version
     - This installation includes npm (Node Package Manager)

  2. Visual Studio Code (VS Code):
     - Visit [https://code.visualstudio.com/download](https://code.visualstudio.com/download)
     - Download and install the version appropriate for your operating system

After installation, verify that Node.js and npm are correctly installed by opening a terminal or command prompt and running:
 

```
node --version
npm --version
```
Output>>>>>

```
Microsoft Windows [Version 10.0.22000.3079]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\system32>node --version
v18.14.2

C:\Windows\system32>npm --version
9.5.0

C:\Windows\system32>

```

**Creating a new React app**
1. Open your terminal or command prompt
2. Navigate to the directory where you want to create your project
3. Run the following command:

```
npm create-react-app client
```
4. Once the installation is complete, navigate into your new project directory:

```
cd client
```
5. Start the development server to ensure everything is working:

```
npm start
```
Your default web browser should open and display the default React app page.

**Setting up the Node.js/Express server**
1. In your terminal. navigate back to your main project directory
2. Create a new directory for your server:

```
mkdir server
cd server
```
3. Initialize a new Node.js project:

```
npm init -y
```
4. Install the necessary dependencies:

```
npm i express bodyParser mongodb mongoose cors dotenv
```
5. Create a new file named index.js in the server directory and add the following basic Express server setup:

```
// index.js
require('dotenv').config();

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

const app = express();

app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());

app.use("/", (req, res) => {
  res.send("Server running.");
});

const port = process.env.PORT || 9000;

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
```
6. Add a start script to your package.json file in the server directory

```
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
  },
```
7. Start your server:

```
npm start build
```
Output>>>>>>

```
PS C:\Users\Matrix\Documents\D.M.F\server> npm start build

> server@1.0.0 start
> nodemon index.js build

[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node index.js build`
Server is running on port 9000
```
 **Configuring the Backend**
create a new .js file in your backend directory and add the following code:

```
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB connected successfully');
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
};

module.exports = connectDB;
```
**Setting up MongoDB connection and Creating environment variables**
1. Log in to your MongoDB Atlas account [https://www.mongodb.com/cloud/atlas/register](https://www.mongodb.com/cloud/atlas/register)
2. Navigate to your cluster's Network Access settings
3. Click on "Add IP Address",
4. To allow connections from any IP address (suitable for development but should be restricted for production), enter:
IP Address: 0.0.0.0/0
Description: Allow access from anywhere
5. Click "Confirm".

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s3alpo7jqm1k92b9ljs1.png)

_Important:_

> Allowing access from 0.0.0.0/0 means your database can be accessed from any IP address. This is convenient for development and testing, but for a production environment, you should restrict access to only the necessary IP addresses or ranges for security reasons.


Create a .env file in your backend directory and add your MongoDB connection string:
```
MONGODB_URI=your_mongodb_connection_string_here
PORT=9000
```
Replace `your_mongodb_connection_string_here`


**Structuring the Express app**
Now, update `index.js` file to incorporate these changes:
```
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const connectDB = require('./db');

const app = express();

// Connect to MongoDB
connectDB();

// Middleware
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());

// Routes
app.get("/", (req, res) => {
  res.send("Server deployed and running on vercel.");
});

// You can add more route files here as your application grows
// app.use('/api/users', require('./routes/users'));
// app.use('/api/posts', require('./routes/posts'));

const port = process.env.PORT || 9000;

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

**Developing the Frontend**
For the purpose of this article, we'll be using the following structure to illustrate how to organize a React frontend. Keep in mind that React is highly flexible, and you can adapt this organization to best suit your project's specific requirements.

**React App Structure**
Let's consider the following structure for our React application:

```
 client/
   ├── src/
   │   ├── components/
   │   ├── services/
   │   ├── styles/
   │   ├── App.js
   │   └── index.js
   ├── package.json
   └── README.md
```

 

- components/: Contains all React components
-  services/: Houses API-related code
-  styles/: Stores CSS files


**Creating Necessary Components**
For this project, I've created several key components. Here's an example of one of the main components, `UserManagement.js`:

```
import React, { useEffect, useState } from 'react';
import api from '../services/api';
import IncomingRequestTable from './IncomingRequestTable';
import MembersTable from './MembersTable';
import VolunteerTable from './VolunteerTable';
import './user-management.css';

const UserManagement = () => {
  const [showIncomingRequests, setShowIncomingRequests] = useState(true);
  const [incomingRequests, setIncomingRequests] = useState([]);
  const [teamMembers, setTeamMembers] = useState([]);
  const [showVolunteers, setShowVolunteers] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchIncomingRequests();
    fetchTeamMembers();
  }, []);

  // Fetch functions and other logic here...

  return (
    <div className="user-management-main-container">
      {/* Component JSX here... */}
    </div>
  );
};

export default UserManagement;
```

> This component manages the display of different user tables and handles data fetching.

**Implementing API Calls to the Backend**
To interact with our backend, I've created an `api.js` file in the services folder. Here's a snippet of how it's structured:

```
const BASE_URL = 'https://localhost:9000';

const handleResponse = async (response) => {
  if (!response.ok) {
    const error = await response.text();
    throw new Error(error);
  }
  return response.json();
};

const api = {
  team: {
    getMembers: (status) => 
      fetch(`${BASE_URL}/get-team-members?status=${status}`)
        .then(handleResponse),
    
    acceptRequest: (userId) =>
      fetch(`${BASE_URL}/accept-request/${userId}`, {
        method: 'POST',
      }).then(handleResponse),
    
    // Other API methods...
  },
  // Other API categories...
};

export default api;
```

> This centralized API structure allows for easy management of all backend requests.

In the components, we use these API calls like this:

```
const fetchIncomingRequests = async () => {
  try {
    const data = await api.team.getMembers('pending');
    setIncomingRequests(data);
  } catch (error) {
    setError('Error fetching incoming requests');
  } finally {
    setLoading(false);
  }
};
```
This approach keeps our components clean and our API calls organized.

**Connecting Frontend and Backend**
In this section, we'll focus on configuring CORS (Cross-Origin Resource Sharing) and testing local communication between our React frontend and Express backend.

Configuring CORS
CORS is a crucial security feature that needs to be properly configured to allow our frontend to communicate with the backend. In  
 `index.js` file, we've already set up CORS:

```
const cors = require('cors');

app.use(cors());

 app.use(cors({
   origin: 'http://localhost:9000'  //to be changed later to vercel url
 }));
```

**Testing Local Communication**
To test the communication between your frontend and backend locally:

Start your backend server:

```
npm start build
```
You should see a message: `Server is running on port 9000`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fm6z91sfndh2f6hoccvd.png)

In your frontend's api.js file, ensure you're using the correct local URL. For this article:
`const BASE_URL = 'http://localhost:9000';`

Start your React development server:
`npm start`

Test an API call, for example, fetching team members:

```
javascriptCopyconst fetchTeamMembers = async () => {
  try {
    const response = await fetch(`${BASE_URL}/get-team-members?status=accepted`);
    const data = await response.json();
    console.log('Team members:', data);
  } catch (error) {
    console.error('Error fetching team members:', error);
  }
};
```

Check your browser's console and network tab to ensure the request is successful and data is being received.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvqffahg1xsw6x7lsoge.png)

If you encounter any CORS errors, double-check your CORS configuration in the backend.

**Preparing for Deployment**
In this section, we'll focus on creating a Vercel configuration file and pushing your code to GitHub repositories, which are crucial steps for deploying your application on Vercel.

**Creating a Vercel Configuration File**
Vercel uses a configuration file named `vercel.json` to specify how to build and deploy your application. Here's the configuration file we'll use:

```
{
    "version": 2,
    "builds": [
      {
        "src": "*.js",
        "use": "@vercel/node"
      }
    ],
    "routes": [
      {
         "src": "/(.*)",
         "dest": "/",
          "methods": ["GET","POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
          "headers": {
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Credentials": "true",
             "Access-Control-Allow-Headers": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
         }
     }
   ]
}
```

This configuration does the following:

- Specifies that we're using version 2 of Vercel's configuration.
- Sets up builds for all JavaScript files using the Node.js runtime.
- Configures routes to handle all HTTP methods and sets up CORS headers.

> Create this file in the root of your backend project and name it `vercel.json`.

**Pushing Code to GitHub Repositories**
Before deploying, you need to push your code to GitHub:
For this project,

1. create two repositories on GitHub: one for your frontend and one for your backend.

2. In your backend directory, initialize a Git repository if you haven't already:

```
git init
```

3. Add your files and commit:

```
git add .
git commit -m "Initial commit for backend"
```

4. Add your GitHub repository as a remote and push:

```
git remote add origin https://github.com/your-username/your-backend-repo.git
git branch -M main
git push -u origin main
```

5. Repeat steps 2-4 for your frontend directory, using the frontend GitHub repository URL.


**Installing and Using Vercel CLI**
To deploy your application using Vercel, you'll need to install and use the Vercel CLI:

Install Vercel CLI globally:

```
npm install -g vercel
```

Verify the installation:

```
`vercel --version`
```

Log in to your Vercel account:

```
vercel login
```


![Vercel Login CLI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwg7qu0kxsxqdo4c860t.png)


![Vercel Login Success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83kgnj5gek9jxvhxfz4y.png)

**Deploying to Vercel**
Before starting, ensure you're logged into your Vercel account.

**Deploying the React Frontend**

In the Vercel dashboard, click on `"Add New"` and select `"Project"` from the dropdown.

![Vercel Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtw2i777oc1p6ot4dmbn.png)

Choose `"Import Git Repository"` and select your frontend repository.

![Selecting Repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2iv0m9efeltiyo188iu.png)


Configure the project:

- Enter a project name
- For Framework Preset, select `"Create React App"`
- Set Root Directory to "./" (if your package.json is in the root)
- Leave Build and Output Settings as default
- Leave Environment Variables as default

![Configurations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdnukm58mn9aqjfcunyb.png)

Click `"Deploy"`

Vercel will now build and deploy your React frontend. Once complete, you'll receive a URL for your deployed frontend.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cqbopsbf69n7pcat8j9.png)


**Deploying the Node.js Backend**

Again, click on `"Add New"` and select `"Project"`.
Choose `"Import Git Repository"` and select your backend repository.

Configure the project:

- Enter a project name
- For Framework Preset, select "Other"
- Set Root Directory to "./" (if your package.json is in the root)
- Leave Build and Output Settings as default
- Configuring `"Environment Variables"`:

Add your environment variables exactly as they appear in your local .env file. For example:

- Key: `MONGODB_URI`
- Value: `mongodb+srv://your_username:your_password@your_cluster.mongodb.net/your_database?retryWrites=true&w=majority`


Add any other necessary environment variables (e.g., PORT, JWT_SECRET, etc.)

Click `"Deploy"`

![Environment Variables setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1ifr2omrttkjh0fje1y.png)

Vercel will begin building your backend code. However, the build may take some time due to environment variables.

![Success for backend deploy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9ae6xbfp46647m3eq7t.png)

![Both front and backend deployments](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dkxgln0tyug39j6isr09.png)


_You've successfully deployed frontend React App and Backend Node js Server to Vercel!!!_


**Important Notes:**

> Make sure your backend code is using environment variables correctly, e.g., `process.env.MONGODB_URI`.
> In your frontend code, update the API base URL to use the deployed backend URL.
> If you make changes to your code, push them to GitHub. Vercel will automatically redeploy your application.
> Always keep your environment variables secret and never commit them to your repository.


**Updating API Endpoints in the Frontend**
After successfully deploying both your frontend and backend, you need to update the API endpoints in your frontend code to point to the deployed backend URL.

In your frontend code, locate your API configuration file (e.g.,` src/services/api.js`).
Update the BASE_URL to your deployed backend URL:

```
const BASE_URL = 'http://localhost:9000'; // Local development
const BASE_URL = 'https://dmfc-server.vercel.app'; // Deployed backend
```
Redeploy your frontend application to Vercel with these changes by pushing to Github.

**Final Testing and Verification**
Now it's time to thoroughly test your deployed application:

Open your deployed frontend URL in a web browser.

Test all functionalities of your application, ensuring they work as expected with the live backend.

Test your application on different devices and browsers to ensure compatibility.

Monitor your Vercel logs and MongoDB Atlas dashboard for any errors or unexpected behavior.

![Frontend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6w5m66jcsl7kxuj88ax.png)


![Backend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ls9h2xw0egtoucrbcg6k.png)


**Conclusion**
Congratulations! You've successfully deployed your MERN stack application to Vercel. Here's a summary of what we've accomplished:

- Set up a React frontend and Node.js/Express backend with MongoDB integration.
- Prepared our application for deployment by creating necessary configuration files.
- Deployed both frontend and backend to Vercel.
- Configured environment variables to ensure secure and proper functionality.
- Updated API endpoints and performed final testing.

> Your application is now live and accessible to users worldwide