This comprehensive guide walks you through building a robust backend REST API using Node.js, Express, and MongoDB, centered around a Goals project. In this project, you’ll build an API to manage “goals” simple text-based records representing tasks or objectives. The guide includes step-by-step instructions on setting up the project, configuring environment variables, connecting to MongoDB, designing the Goal model with Mongoose, and creating REST API endpoints (CRUD) to create, read, update, and delete goals. Along the way, you’ll also learn best practices in structuring your application, handling errors gracefully, and preparing your backend for integration with a frontend app.
🔧 1. Setting Up the Project
✅ Initialize Node Project
npm init -y npm install express mongoose dotenv express-async-handler npm install nodemon --save-dev
Add to package.json.
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
✅ Create .env
PORT = 5000
MONGO_URI = your_mongodb_connection_string
🚀 2. server.js — App Entry Point
const express = require('express')
const dotenv = require('dotenv').config()
const connectDB = require('./config/db')
const goalRoutes = require('./routes/goalRoutes')
const { errorHandler } = require('./middleware/errorMiddleware')
connectDB()
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use('/api/goals', goalRoutes)
app.use(errorHandler)
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started on ${PORT}`))
🔗 3. Database Connection — /config/db.js
const mongoose = require('mongoose')
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI) console.log(`MongoDB Connected: ${conn.connection.host}`)
} catch (error) {
console.error(error) process.exit(1)
}
}
module.exports = connectDB
📦 4. Goal Model — /models/goalModel.js
const mongoose = require('mongoose') const goalSchema = mongoose.Schema({
text: {
type: String,
required: [true, 'Please add a text value'],
},
}, {
timestamps: true
}) module.exports = mongoose.model('Goal', goalSchema)
🧠 5. Goal Controller — /controllers/goalController.js
const asyncHandler = require('express-async-handler') const Goal = require('../models/goalModel')
// @desc Get goals
exports.getGoals = asyncHandler(async (req, res) => {
const goals = await Goal.find() res.status(200).json(goals)
})
// @desc Create goal
exports.setGoal = asyncHandler(async (req, res) => {
if (!req.body.text) {
res.status(400) throw new Error('Please add a text field')
}
const goal = await Goal.create({
text: req.body.text
}) res.status(201).json(goal)
})
// @desc Update goal
exports.updateGoal = asyncHandler(async (req, res) => {
const goal = await Goal.findById(req.params.id) if (!goal) {
res.status(404) throw new Error('Goal not found')
}
const updatedGoal = await Goal.findByIdAndUpdate(req.params.id, req.body, {
new: true
}) res.status(200).json(updatedGoal)
})
// @desc Delete goal
exports.deleteGoal = asyncHandler(async (req, res) => {
const goal = await Goal.findById(req.params.id) if (!goal) {
res.status(404) throw new Error('Goal not found')
} await goal.deleteOne()
res.status(200).json({
id: req.params.id
})
})
Join the conversation! Your thoughts help the community grow.