The MERN stack has emerged as a powerful framework for modern web development, combining MongoDB, Express.js, React.js, and Node.js. This guide will walk you through setting up a MERN stack application efficiently, leveraging a pre-configured repository and streamlined development environment management.
The MERN stack comprises four key technologies:
Each component plays a crucial role in creating dynamic and interactive web applications.
We'll use a Creator Relationship Management (CRM) App as our example. This full-stack application is designed for influencers, podcasters, and creators to manage client relationships efficiently.
Follow the MongoDB Atlas Getting Started guide to create a cluster and obtain your MongoDB URI.
Note: If you encounter connection issues, allow access from anywhere in your cluster's Network Access settings.
We'll use Daytona to automate the setup of a Node.js dev environment with necessary tools and port forwarding based on our starter project.
Create a workspace:
daytona create https://github.com/daytonaio-experiments/starter-mern-saas.git
Set your preferred IDE:
daytona ide
Open the workspace:
daytona code
Start the backend server:
Create a .env
file in the backend directory with your MongoDB UR (you can find it in Atlas dashboard)I:
MONGO_URI="mongodb+srv://<username>:<password>@SOMETHING.SOMETHING.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"
Run the server:
cd backend && npm run dev
Start the frontend:
Create a .env
file in the frontend directory:
VITE_BACKEND_URL="http://localhost:8000/api/customers/"
Run the frontend:
cd frontend && npm run dev
Your MERN app should now be running.
The repository is organized into:
The .devcontainer/devcontainer.json
file provides a predefined development environment. Here's the configuration:
{
"name": "Node.js, Express, React, MongoDB & Tailwind",
"image": "ubuntu:22.04",
"features": {
"ghcr.io/devcontainers/features/common-utils:2.4.7": {
"username": "daytona",
"userUid": 1000,
"userGid": 1000,
"configureZshAsDefaultShell": true
},
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
"version": "lts",
"nvmVersion": "0.40.0"
},
"ghcr.io/devcontainers/features/git:1": {}
},
"overrideFeatureInstallOrder": [
"ghcr.io/devcontainers/features/common-utils",
"ghcr.io/devcontainers/features/git",
"ghcr.io/devcontainers/features/node"
],
"portsAttributes": {
"5174": {
"label": "Frontend",
"onAutoForward": "notify"
},
"8000": {
"label": "Backend",
"onAutoForward": "ignore"
},
"27017": {
"label": "MongoDB",
"onAutoForward": "ignore"
}
},
"customizations": {
"vscode": {
"extensions": [
"mongodb.mongodb-vscode",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"davidanson.vscode-markdownlint"
]
}
},
"workspaceFolder": "/workspaces/starter-mern-saas",
"onCreateCommand": "npm install -g nodemon",
"postCreateCommand": "cd backend && npm install && cd ../frontend && npm install",
"remoteUser": "daytona"
}
This configuration ensures a consistent development environment across different machines.
productRoutes.js
):const express = require('express');
const router = express.Router();
const productService = require('../service/productService');
router.get('/products', async (req, res) => {
try {
const products = await productService.getAllProducts();
res.status(200).json(products);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
productService.js
):class ProductService {
async getAllProducts() {
// Logic to fetch all products
}
}
module.exports = ProductService;
app.js
:const productRoutes = require('./routes/productRoutes');
app.use('/api', productRoutes);
ProductList.jsx
):import React, { useEffect, useState } from 'react';
import axios from 'axios';
const ProductList = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const response = await axios.get('/api/products');
setProducts(response.data);
};
fetchProducts();
}, []);
return (
<div>
<h1>Products</h1>
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
};
export default ProductList;
App.jsx
:import React from 'react';
import ProductList from './components/ProductList';
function App() {
return (
<div className="App">
<ProductList />
</div>
);
}
export default App;
You've now set up a MERN stack application, cloned an example repository, and learned how to customize both the backend and frontend. This streamlined setup process allows you to focus on developing and enhancing your application without worrying about environmental inconsistencies.
Happy coding, and enjoy your development journey with the MERN stack!