DEV Community

Cover image for Setting up a full-stack MERN (MongoDB, Express, React, Node.js) app for deployment on Vercel.
Israel Toki
Israel Toki

Posted on • Originally published at toki-adedapo.com

Setting up a full-stack MERN (MongoDB, Express, React, Node.js) app for deployment on Vercel.

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

  2. Visual Studio Code (VS Code):

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

node --version
npm --version
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Once the installation is complete, navigate into your new project directory:
cd client
Enter fullscreen mode Exit fullscreen mode
  1. Start the development server to ensure everything is working:
npm start
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Initialize a new Node.js project:
npm init -y
Enter fullscreen mode Exit fullscreen mode
  1. Install the necessary dependencies:
npm i express bodyParser mongodb mongoose cors dotenv
Enter fullscreen mode Exit fullscreen mode
  1. 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}`);
});
Enter fullscreen mode Exit fullscreen mode
  1. 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"
  },
Enter fullscreen mode Exit fullscreen mode
  1. Start your server:
npm start build
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Setting up MongoDB connection and Creating environment variables

  1. Log in to your MongoDB Atlas account 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

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
Enter fullscreen mode Exit fullscreen mode

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}`);
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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
 }));
Enter fullscreen mode Exit fullscreen mode

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

Start your backend server:

npm start build
Enter fullscreen mode Exit fullscreen mode

You should see a message: Server is running on port 9000

Image description

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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

Image description

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"
         }
     }
   ]
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Add your files and commit:
git add .
git commit -m "Initial commit for backend"
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode

Verify the installation:

`vercel --version`
Enter fullscreen mode Exit fullscreen mode

Log in to your Vercel account:

vercel login
Enter fullscreen mode Exit fullscreen mode

Vercel Login CLI

Vercel Login Success

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

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

Selecting Repo

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

Click "Deploy"

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

Image description

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

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

Success for backend deploy

Both front and backend deployments

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
Enter fullscreen mode Exit fullscreen mode

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

Backend

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

Top comments (0)