Backend Complete Guide Main
Backend Complete Guide Main
STEP 1 — [Link]
Defines all dependencies. Run npm install after creating this.
{
"name": "devops-monitor-backend",
"version": "1.0.0",
"description": "AI-Powered DevOps Monitoring Platform Backend",
"main": "src/[Link]",
"scripts": {
"start": "node src/[Link]",
"dev": "nodemon src/[Link]"
},
"dependencies": {
"axios": "^1.6.0",
"bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0",
"[Link]": "^4.6.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
about:blank 1/24
5/17/26, 8:02 PM Untitled
}
}
STEP 2 — .env
# Server
PORT=5000
NODE_ENV=development
# MongoDB
MONGO_URI=mongodb://localhost:27017/devops-monitor
# JWT
JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
JWT_EXPIRE=7d
STEP 3 — src/config/[Link]
[Link] = connectDB;
about:blank 2/24
5/17/26, 8:02 PM Untitled
STEP 4 — src/config/[Link]
Sets up [Link] so the backend can push live updates to the React frontend.
We export the io instance so any controller can emit events.
let io; // We store io here so other files can import and use it
[Link]("disconnect", () => {
[Link](`🔌 Client disconnected: ${[Link]}`);
});
});
return io;
};
STEP 5 — src/models/[Link]
Stores ML prediction results (anomaly scores, failure probabilities).
This is the only model not yet created.
timestamp: {
type: Date,
default: [Link],
},
});
STEP 6 — src/utils/[Link]
A single place to store magic strings and numbers.
Changing a constant here updates it everywhere.
about:blank 4/24
5/17/26, 8:02 PM Untitled
// Alert types
const ALERT_TYPES = {
CPU: "CPU",
MEMORY: "MEMORY",
DISK: "DISK",
SERVICE_DOWN: "SERVICE_DOWN",
ML_ANOMALY: "ML_ANOMALY",
};
STEP 7 — src/utils/[Link]
Creates a signed JWT token for a user.
Called during register and login.
/**
* Generates a JWT and sets it as an HTTP-only cookie on the response.
* HTTP-only means JavaScript on the frontend cannot read the cookie
about:blank 5/24
5/17/26, 8:02 PM Untitled
[Link]("token", token, {
httpOnly: true, // Not accessible via JavaScript
secure: [Link].NODE_ENV === "production", // HTTPS only in production
sameSite: "strict", // Prevents CSRF attacks
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in milliseconds
});
return token;
};
[Link] = generateToken;
STEP 8 — src/utils/[Link]
/**
* Generic pagination helper.
*
* Usage in a controller:
* const result = await paginate(Log, [Link], { serviceName: "api" });
*
* Query params supported: ?page=1&limit=20
*
* @param {Model} Model - A Mongoose model (e.g., Log, Metric)
* @param {Object} query - [Link] from Express
* @param {Object} filter - MongoDB filter object (e.g., { severity: "ERROR" })
* @returns {Object} { data, currentPage, totalPages, totalCount }
*/
const paginate = async (Model, query = {}, filter = {}) => {
const page = parseInt([Link]) || 1; // Default: page 1
const limit = parseInt([Link]) || 20; // Default: 20 items per page
const skip = (page - 1) * limit; // How many documents to skip
about:blank 6/24
5/17/26, 8:02 PM Untitled
return {
data,
currentPage: page,
totalPages: [Link](totalCount / limit),
totalCount,
};
};
[Link] = paginate;
STEP 9 — src/middleware/[Link]
Wraps async controller functions to catch errors automatically.
Without this you'd need try/catch in every single controller.
/**
* Wraps an async function and passes any error to Express's error handler.
*
* Instead of:
* async (req, res, next) => { try { ... } catch(e) { next(e) } }
*
* You write:
* catchAsync(async (req, res, next) => { ... })
*/
const catchAsync = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch(next); // .catch(next) sends error to errorMiddleware
};
};
[Link] = catchAsync;
STEP 10 — src/middleware/[Link]
Global error handler. Express calls this whenever next(error) is called.
Keeps all error formatting in one place.
// JWT errors
if ([Link] === "JsonWebTokenError") {
statusCode = 401;
message = "Invalid token. Please log in again.";
}
if ([Link] === "TokenExpiredError") {
statusCode = 401;
message = "Token expired. Please log in again.";
}
[Link](statusCode).json({
success: false,
message,
// Only show the stack trace in development (hides internal details in production)
...([Link].NODE_ENV === "development" && { stack: [Link] }),
});
};
[Link] = errorMiddleware;
STEP 11 — src/middleware/[Link]
Protects routes. Only logged-in users with valid tokens can access protected endpoints.
about:blank 8/24
5/17/26, 8:02 PM Untitled
if (!token) {
return [Link](401).json({
success: false,
message: "Not authorized. Please log in.",
});
}
// Attach the full user object to the request for use in controllers
[Link] = await [Link]([Link]).select("-password");
about:blank 9/24
5/17/26, 8:02 PM Untitled
STEP 12 — src/ml/[Link]
/**
* Send metric data to the ML service for anomaly detection.
* Returns prediction result from the ML model.
*
* @param {Object} metricData - { serverId, cpuUsage, memoryUsage, diskUsage }
*/
const detectAnomaly = async (metricData) => {
try {
const response = await [Link](`${ML_BASE_URL}/predict/anomaly`, metricData, {
timeout: 5000, // 5 second timeout — don't block the API if ML is slow
});
return [Link];
} catch (error) {
// If ML service is down, log the error but don't crash the main API
[Link]("⚠️ ML Service (anomaly) unavailable:", [Link]);
return null; // Return null so the caller can handle gracefully
}
};
/**
* Send metric data to the ML service for failure prediction.
* Returns crash probability and recommendation.
*
* @param {Object} metricData - { serverId, cpuUsage, memoryUsage, diskUsage }
*/
const predictFailure = async (metricData) => {
try {
const response = await [Link](`${ML_BASE_URL}/predict/failure`, metricData, {
timeout: 5000,
});
return [Link];
} catch (error) {
[Link]("⚠️ ML Service (failure) unavailable:", [Link]);
return null;
}
};
about:blank 10/24
5/17/26, 8:02 PM Untitled
/**
* Send a log message to the ML service for classification.
* Returns the log category (e.g., "Database Error", "Network Issue").
*
* @param {string} logMessage - The raw log message text
*/
const classifyLog = async (logMessage) => {
try {
const response = await [Link](
`${ML_BASE_URL}/predict/classify-log`,
{ message: logMessage },
{ timeout: 5000 }
);
return [Link];
} catch (error) {
[Link]("⚠️ ML Service (classify) unavailable:", [Link]);
return null;
}
};
STEP 13 — src/controllers/[Link]
Handles CRUD operations for logs.
// GET /api/logs
// Returns paginated logs. Supports ?page=1&limit=20&severity=ERROR
const getLogs = catchAsync(async (req, res) => {
// Build a filter from query params
const filter = {};
if ([Link]) [Link] = [Link];
if ([Link]) [Link] = [Link];
// GET /api/logs/:id
const getLogById = catchAsync(async (req, res) => {
const log = await [Link]([Link]);
about:blank 11/24
5/17/26, 8:02 PM Untitled
if (!log) {
return [Link](404).json({ success: false, message: "Log not found" });
}
// POST /api/logs
// Called by the Python monitoring agent to push new logs
const createLog = catchAsync(async (req, res) => {
const { serviceName, severity, message } = [Link];
// Emit the new log to all connected frontend clients in real time
try {
getIO().emit("new_log", log);
} catch (e) {
// Socket not critical — don't fail the request if it errors
}
if (!log) {
return [Link](404).json({ success: false, message: "Log not found" });
}
STEP 14 — src/controllers/[Link]
about:blank 12/24
5/17/26, 8:02 PM Untitled
// GET /api/metrics
// Supports filtering by ?serverId=server-01
const getMetrics = catchAsync(async (req, res) => {
const filter = {};
if ([Link]) [Link] = [Link];
// GET /api/metrics/server/:serverId
// Returns the latest metric reading for a specific server
const getLatestMetricByServer = catchAsync(async (req, res) => {
const metric = await [Link]({ serverId: [Link] }).sort({
timestamp: -1,
});
if (!metric) {
return res
.status(404)
.json({ success: false, message: "No metrics found for this server" });
}
// POST /api/metrics
// Called by the Python monitoring agent every few seconds
const createMetric = catchAsync(async (req, res) => {
const { serverId, cpuUsage, memoryUsage, diskUsage } = [Link];
about:blank 13/24
5/17/26, 8:02 PM Untitled
try {
getIO().emit("new_alert", alert);
} catch (e) {}
}
STEP 15 — src/controllers/[Link]
// GET /api/alerts
// Supports ?resolved=false&severity=CRITICAL
const getAlerts = catchAsync(async (req, res) => {
const filter = {};
// Filter by resolved status — "false" string from query param needs conversion
if ([Link] !== undefined) {
[Link] = [Link] === "true";
}
if ([Link]) [Link] = [Link];
about:blank 14/24
5/17/26, 8:02 PM Untitled
// GET /api/alerts/:id
const getAlertById = catchAsync(async (req, res) => {
const alert = await [Link]([Link]);
if (!alert) {
return [Link](404).json({ success: false, message: "Alert not found" });
}
// POST /api/alerts
// Manually create an alert (can also be triggered by metricController automatically)
const createAlert = catchAsync(async (req, res) => {
const { type, message, severity } = [Link];
// PATCH /api/alerts/:id/resolve
// Mark an alert as resolved (done investigating)
const resolveAlert = catchAsync(async (req, res) => {
const alert = await [Link](
[Link],
{ resolved: true },
{ new: true } // Return the updated document
);
if (!alert) {
return [Link](404).json({ success: false, message: "Alert not found" });
}
STEP 16 — src/controllers/[Link]
Calls the ML service and saves predictions to MongoDB.
about:blank 15/24
5/17/26, 8:02 PM Untitled
// GET /api/predictions
// Supports ?serverId=server-01&predictionType=anomaly
const getPredictions = catchAsync(async (req, res) => {
const filter = {};
if ([Link]) [Link] = [Link];
if ([Link]) [Link] = [Link];
// POST /api/predictions/analyze
// Manually trigger an ML analysis for a server
// Body: { serverId, cpuUsage, memoryUsage, diskUsage, predictionType }
const analyzeServer = catchAsync(async (req, res) => {
const { serverId, cpuUsage, memoryUsage, diskUsage, predictionType } = [Link];
if (!serverId || !predictionType) {
return [Link](400).json({
success: false,
message: "serverId and predictionType are required",
});
}
about:blank 16/24
5/17/26, 8:02 PM Untitled
success: false,
message: "ML service is currently unavailable. Please try again later.",
});
}
STEP 17 — src/routes/[Link]
[Link] = router;
STEP 18 — src/routes/[Link]
about:blank 17/24
5/17/26, 8:02 PM Untitled
const {
getLogs,
getLogById,
createLog,
deleteLog,
} = require("../controllers/logController");
const { protect, authorize } = require("../middleware/authMiddleware");
[Link]("/").get(getLogs).post(createLog);
router
.route("/:id")
.get(getLogById)
.delete(authorize("Admin"), deleteLog); // Only Admins can delete logs
[Link] = router;
STEP 19 — src/routes/[Link]
[Link](protect);
[Link]("/").get(getMetrics).post(createMetric);
[Link] = router;
STEP 20 — src/routes/[Link]
about:blank 18/24
5/17/26, 8:02 PM Untitled
getAlerts,
getAlertById,
createAlert,
resolveAlert,
} = require("../controllers/alertController");
const { protect } = require("../middleware/authMiddleware");
[Link](protect);
[Link]("/").get(getAlerts).post(createAlert);
[Link]("/:id").get(getAlertById);
[Link]("/:id/resolve", resolveAlert);
[Link] = router;
STEP 21 — src/routes/[Link]
[Link](protect);
[Link]("/", getPredictions);
[Link]("/analyze", analyzeServer);
[Link] = router;
STEP 22 — src/[Link]
// Import routes
const authRoutes = require("./routes/authRoutes");
const logRoutes = require("./routes/logRoutes");
about:blank 19/24
5/17/26, 8:02 PM Untitled
[Link] = app;
STEP 23 — src/[Link]
about:blank 20/24
5/17/26, 8:02 PM Untitled
The entry point. Loads environment variables, connects to DB, starts HTTP server, and
attaches [Link].
STEP 24 — Dockerfile
# Copy package files first (Docker caches this layer — speeds up rebuilds)
COPY package*.json ./
about:blank 21/24
5/17/26, 8:02 PM Untitled
# File Status
1 [Link] ✅ Create
2 .env ✅ Create
3 src/config/[Link] ✅ Create
4 src/config/[Link] ✅ Create
10 src/models/[Link] ✅ Create
11 src/utils/[Link] ✅ Create
12 src/utils/[Link] ✅ Create
13 src/utils/[Link] ✅ Create
15 src/middleware/[Link] ✅ Create
about:blank 22/24
5/17/26, 8:02 PM Untitled
# File Status
17 src/ml/[Link] ✅ Create
19 src/controllers/[Link] ✅ Create
20 src/controllers/[Link] ✅ Create
21 src/controllers/[Link] ✅ Create
22 src/controllers/[Link] ✅ Create
23 src/routes/[Link] ✅ Create
24 src/routes/[Link] ✅ Create
25 src/routes/[Link] ✅ Create
26 src/routes/[Link] ✅ Create
27 src/routes/[Link] ✅ Create
28 src/[Link] ✅ Create
29 src/[Link] ✅ Create
30 Dockerfile ✅ Create
Why [Link] wraps the HTTP server: [Link] needs the raw [Link] object, not the Express
app, so it can handle the WebSocket upgrade handshake.
Why detectAnomaly() isn't awaited in metricController : ML inference can be slow. By not awaiting
it, the metric is saved to MongoDB and the API responds immediately. The ML check runs in parallel.
This pattern is called "fire and forget."
about:blank 23/24
5/17/26, 8:02 PM Untitled
Why paginate() uses [Link] : Running the data query and the count query in parallel (instead
of one after the other) cuts the response time in half.
about:blank 24/24