Project Setup
Setting up a new Node.js + Express backend project from scratch — folder structure, package.json, and initial config.
Project Setup
This is the starting point for every backend project in this section — the folder structure and config files we build on for schemas, auth, and everything else.
Initialize the Project
mkdir my-app-backend
cd my-app-backend
npm init -yInstall the core packages every backend needs:
npm install express mongoose dotenv cors
npm install -D nodemonexpress— the web server frameworkmongoose— MongoDB connection and schemasdotenv— load environment variables from.envcors— allow the frontend to talk to this backendnodemon— auto-restarts the server on file changes (dev only)
Folder Structure
This is the standard structure used across this entire section — every file we write later goes in one of these folders.
my-app-backend/
├── src/
│ ├── config/
│ │ └── db.js # MongoDB connection
│ ├── models/
│ │ ├── user.model.js
│ │ └── post.model.js
│ ├── controllers/
│ │ ├── auth.controller.js
│ │ └── post.controller.js
│ ├── routes/
│ │ ├── auth.routes.js
│ │ └── post.routes.js
│ ├── middleware/
│ │ ├── auth.middleware.js
│ │ └── error.middleware.js
│ ├── utils/
│ │ └── asyncHandler.js
│ └── app.js # Express app setup
├── server.js # entry point
├── .env
├── .env.example
├── .gitignore
└── package.jsonmkdir -p src/config src/models src/controllers src/routes src/middleware src/utilspackage.json — Scripts
Open package.json and add a type and scripts section:
{
"name": "my-app-backend",
"version": "1.0.0",
"type": "module",
"main": "server.js",
"scripts": {
"dev": "nodemon server.js",
"start": "node server.js"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"mongoose": "^8.7.0"
},
"devDependencies": {
"nodemon": "^3.1.7"
}
}"type": "module" enables ES module syntax — import/export instead of require/module.exports. This is the modern standard and what every file in this section uses.
.env and .env.example
# .env — your actual secrets, never committed to git
PORT=5000
MONGO_URI=mongodb+srv://username:password@cluster.mongodb.net/myapp
JWT_SECRET=your_super_secret_key_here
JWT_REFRESH_SECRET=another_super_secret_key
NODE_ENV=development
CLIENT_URL=http://localhost:5173# .env.example — committed to git, shows what variables are needed
PORT=5000
MONGO_URI=
JWT_SECRET=
JWT_REFRESH_SECRET=
NODE_ENV=
CLIENT_URL=.env.example has no real values — it just documents what environment variables the project needs. Anyone cloning the repo copies it to .env and fills in their own values.
.gitignore
# .gitignore
node_modules/
.env
.DS_Store
*.log
dist/
build/Never commit node_modules or .env — the first is huge and reproducible, the second contains secrets.
Verify the Setup
Create a minimal server.js just to confirm everything works — we replace this properly in the next file.
// server.js
import express from "express";
const app = express();
const PORT = process.env.PORT || 5000;
app.get("/", (req, res) => {
res.send("Server is running");
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});npm run devVisit http://localhost:5000 — you should see "Server is running".
This server.js is temporary. The next file — Server Setup — replaces it with the real structure: app.js for Express configuration and server.js as a thin entry point that connects to MongoDB before starting the server.
Summary
npm init -ythen installexpress mongoose dotenv corsplusnodemonas a dev dependency"type": "module"inpackage.jsonenables modernimport/exportsyntax- Standard folder structure —
config,models,controllers,routes,middleware,utils .envholds real secrets — never committed —.env.exampledocuments what's needednpm run devstarts the server with auto-restart via nodemon- Next file connects this setup to MongoDB and builds out the real
app.js