Backend API Documentation & Code Review
December 2025
Node.js | Express.js | MongoDB | JWT Authentication
This is a Smart Home IoT Server built using Node.js and Express.js framework. The server provides a RESTful API for managing smart home devices like lights, fans, air conditioners, TVs, sensors, and door locks. Users can register, login, add devices, control them remotely, and view activity logs.
The application uses MongoDB as the database for storing user information, device data, and activity logs. JWT (JSON Web Token) is used for secure authentication, ensuring that only authorized users can access and control their devices.
smart-home-node-server/
├── package.json
├── README.md
├── .env
└── src/
├── index.js (Main entry point)
├── middleware/
│ └── auth.js (JWT authentication middleware)
├── models/
│ ├── User.js (User schema)
│ ├── Device.js (Device schema)
│ └── Log.js (Activity log schema)
└── routes/
├── index.js (Route aggregator)
├── auth.routes.js (Authentication routes)
├── device.routes.js (Device management routes)
└── log.routes.js (Log routes)
| Technology | Purpose | Version |
|---|---|---|
| Node.js | Runtime Environment | Latest |
| Express.js | Web Framework | 4.22.1 |
| MongoDB | Database | Latest |
| Mongoose | MongoDB ODM | 8.0.0 |
| JWT | Authentication Tokens | 9.0.3 |
| bcrypt | Password Hashing | 6.0.0 |
| cors | Cross-Origin Resource Sharing | 2.8.5 |
| dotenv | Environment Variables | 16.3.1 |
The User model stores user registration information including name, email, and encrypted password.
const mongoose = require("mongoose");
const userSchema = mongoose.Schema(
{
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true, minlength: 6 },
},
{ timestamps: true }
);
module.exports = mongoose.model("User", userSchema);
The Device model represents smart home devices. Each device belongs to a user. Temperature and mode fields are only added for AC type devices.
const mongoose = require("mongoose");
const deviceSchema = mongoose.Schema(
{
name: { type: String, required: true },
type: { type: String, required: true },
room: { type: String, required: true },
status: { type: String, default: "off" },
temperature: { type: Number },
mode: { type: String },
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
},
{ timestamps: true }
);
module.exports = mongoose.model("Device", deviceSchema);
temperature and mode fields are only added when creating an AC device. Other devices (light, fan, tv, etc.) will not have these fields.
The Log model tracks all device activities including turning devices on/off and changing settings.
const mongoose = require("mongoose");
const logSchema = mongoose.Schema(
{
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
device: { type: mongoose.Schema.Types.ObjectId, ref: "Device", required: true },
action: { type: String, required: true },
details: { type: Object, default: {} },
},
{ timestamps: true }
);
module.exports = mongoose.model("Log", logSchema);
The authentication system uses JWT tokens for secure user authentication. When a user logs in successfully, they receive a JWT token which must be included in subsequent API requests.
This middleware verifies the JWT token from the Authorization header and extracts the user ID for protected routes.
const jwt = require("jsonwebtoken");
const auth = (req, res, next) => {
try {
let token = req.headers.authorization;
if (!token) {
return res.status(401).send({ message: "No token provided" });
}
let decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userId = decoded.userid;
next();
} catch (err) {
res.status(401).send({ message: "Invalid token" });
}
};
module.exports = auth;
| # | Method | URL | Description | Auth Required |
|---|---|---|---|---|
| 1 | GET | /api/health | Health check | No |
| 2 | POST | /api/auth/register | Register new user | No |
| 3 | POST | /api/auth/login | Login & get token | No |
| 4 | GET | /api/devices | Get all devices | Yes |
| 5 | POST | /api/devices | Create new device | Yes |
| 6 | PATCH | /api/devices/:id/toggle | Toggle device on/off | Yes |
| 7 | PATCH | /api/devices/:id/ac-settings | Update AC temp & mode | Yes |
| 8 | DELETE | /api/devices/:id | Delete a device | Yes |
| 9 | GET | /api/logs | Get all activity logs | Yes |
| 10 | GET | /api/logs/:deviceId | Get logs for specific device | Yes |
https://smart-home-iot-mern-api.onrender.com
https://smart-home-iot-mern-api.onrender.com/api/health
https://smart-home-iot-mern-api.onrender.com/api/auth/register
https://smart-home-iot-mern-api.onrender.com/api/auth/login
https://smart-home-iot-mern-api.onrender.com/api/devices
https://smart-home-iot-mern-api.onrender.com/api/devices/6938364ad1ae47447f9298cf/toggle
https://smart-home-iot-mern-api.onrender.com/api/devices/6938364ad1ae47447f9298cf/ac-settings
https://smart-home-iot-mern-api.onrender.com/api/devices/6938364ad1ae47447f9298cf
https://smart-home-iot-mern-api.onrender.com/api/logs
https://smart-home-iot-mern-api.onrender.com/api/logs/6938364ad1ae47447f9298cf
POST/api/auth/register
Register a new user account
Request Body:
{ "name": "John Doe", "email": "john@example.com", "password": "password123" }
Response:
{ "success": true, "message": "User created" }
POST/api/auth/login
Login and receive JWT token
Request Body:
{ "email": "john@example.com", "password": "password123" }
Response:
{ "success": true, "message": "Login verified", "token": "eyJhbGciOiJIUzI1..." }
GET/api/devices
Get all devices for the authenticated user
Headers: Authorization: <jwt_token>
Response:
{ "success": true, "devices": [...] }
POST/api/devices
Create a new device
Headers: Authorization: <jwt_token>
Request Body:
{ "name": "Living Room AC", "type": "ac", "room": "Living Room" }
Response:
{ "success": true, "message": "Device created", "device": {...} }
PATCH/api/devices/:id/toggle
Toggle device on/off status
Headers: Authorization: <jwt_token>
Response:
{ "success": true, "device": { "status": "on", ... } }
Note: Status returns "on" or "off" (not true/false)
PATCH/api/devices/:id/ac-settings
Update AC temperature and mode (AC must be ON)
Headers: Authorization: <jwt_token>
Request Body:
{ "temperature": 22, "mode": "cool" }
Response:
{ "success": true, "device": {...} }
Error (if AC is OFF):
{ "success": false, "message": "Turn on the AC first to change settings" }
DELETE/api/devices/:id
Delete a device
Headers: Authorization: <jwt_token>
Response:
{ "success": true, "message": "Device deleted" }
GET/api/logs
Get all activity logs for the authenticated user
Headers: Authorization: <jwt_token>
Response:
{ "success": true, "logs": [...] }
GET/api/logs/:deviceId
Get logs for a specific device
Headers: Authorization: <jwt_token>
Response:
{ "success": true, "logs": [...] }
The main entry point of the application that sets up Express server, connects to MongoDB, and configures routes.
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const dotenv = require("dotenv");
dotenv.config();
const app = express();
app.use(cors());
app.use(express.json());
const PORT = process.env.PORT || 5001;
const DB_URL = process.env.DB_URL;
const DB_NAME = process.env.DB_NAME;
mongoose
.connect(DB_URL + DB_NAME)
.then(() => console.log("MongoDB connected"))
.catch((err) => console.log("MongoDB error:", err.message));
app.get("/api/health", (req, res) => {
res.json({ message: "Smart Home API is running" });
});
const routes = require("./routes");
app.use("/api", routes);
app.use((req, res) => {
res.status(404).json({ success: false, message: "Route not found" });
});
app.use((err, req, res, next) => {
res.status(500).json({ success: false, message: err.message });
});
app.listen(PORT, () => {
console.log("Server running on port " + PORT);
});
const express = require("express");
const User = require("../models/User");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const router = express.Router();
router.post("/register", async (req, res) => {
try {
let user = req.body;
const hash = await bcrypt.hash(user.password, 10);
user.password = hash;
await User.create(user);
res.status(201).send({ success: true, message: "User created" });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.post("/login", async (req, res) => {
try {
let userCred = req.body;
let user = await User.findOne({ email: userCred.email });
if (user) {
let match = await bcrypt.compare(userCred.password, user.password);
if (match) {
let token = jwt.sign({ userid: user._id }, process.env.JWT_SECRET);
res.send({ success: true, message: "Login verified", token });
} else {
res.status(401).send({ success: false, message: "Password is wrong" });
}
} else {
res.status(404).send({ success: false, message: "User not found" });
}
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
module.exports = router;
const express = require("express");
const Device = require("../models/Device");
const Log = require("../models/Log");
const auth = require("../middleware/auth");
const router = express.Router();
router.get("/", auth, async (req, res) => {
try {
const devices = await Device.find({ user: req.userId });
res.send({ success: true, devices });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.post("/", auth, async (req, res) => {
try {
const device = { ...req.body, user: req.userId };
// Add temperature and mode only for AC devices
if (device.type === "ac") {
device.temperature = device.temperature || 24;
device.mode = device.mode || "cool";
}
const newDevice = await Device.create(device);
res.status(201).send({ success: true, message: "Device created", device: newDevice });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.patch("/:id/toggle", auth, async (req, res) => {
try {
const device = await Device.findOne({ _id: req.params.id, user: req.userId });
if (!device) {
return res.status(404).send({ success: false, message: "Device not found" });
}
device.status = device.status === "on" ? "off" : "on";
await device.save();
await Log.create({
user: req.userId,
device: device._id,
action: device.status === "on" ? "TURN_ON" : "TURN_OFF",
});
res.send({ success: true, device });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.patch("/:id/ac-settings", auth, async (req, res) => {
try {
const device = await Device.findOne({ _id: req.params.id, user: req.userId });
if (!device) {
return res.status(404).send({ success: false, message: "Device not found" });
}
if (device.type !== "ac") {
return res.status(400).send({ success: false, message: "This is not an AC device" });
}
if (device.status === "off") {
return res.status(400).send({ success: false, message: "Turn on the AC first to change settings" });
}
const { temperature, mode } = req.body;
if (temperature) device.temperature = temperature;
if (mode) device.mode = mode;
await device.save();
await Log.create({
user: req.userId,
device: device._id,
action: "AC_SETTINGS_CHANGED",
details: { temperature, mode },
});
res.send({ success: true, device });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.delete("/:id", auth, async (req, res) => {
try {
const deleted = await Device.findOneAndDelete({
_id: req.params.id,
user: req.userId,
});
if (!deleted) {
return res.status(404).send({ success: false, message: "Device not found" });
}
res.send({ success: true, message: "Device deleted" });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
module.exports = router;
const express = require("express");
const Log = require("../models/Log");
const auth = require("../middleware/auth");
const router = express.Router();
router.get("/", auth, async (req, res) => {
try {
let logs = await Log.find({ user: req.userId })
.populate("device", "name type")
.sort({ createdAt: -1 });
res.send({ success: true, logs });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
router.get("/:deviceId", auth, async (req, res) => {
try {
let logs = await Log.find({
user: req.userId,
device: req.params.deviceId
}).sort({ createdAt: -1 });
res.send({ success: true, logs });
} catch (err) {
res.status(500).send({ success: false, message: "Server error" });
}
});
module.exports = router;
Create a .env file in the root directory with the following variables:
PORT=5001
DB_URL=mongodb://localhost:27017/
DB_NAME=smart_home_db
JWT_SECRET=your_secret_key_here
| Command | Description |
|---|---|
npm install | Install all dependencies |
npm run dev | Run in development mode with nodemon |
npm start | Run in production mode |
Smart Home IoT Server Documentation
© 2025 Ashutosh Rai. All rights reserved.