🏠 Smart Home IoT Server

Backend API Documentation & Code Review

Developed by: Ashutosh Rai

December 2025

Node.js | Express.js | MongoDB | JWT Authentication

📋 Table of Contents

1. Project Overview

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.

2. Project Structure

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)
    

3. Technologies Used

TechnologyPurposeVersion
Node.jsRuntime EnvironmentLatest
Express.jsWeb Framework4.22.1
MongoDBDatabaseLatest
MongooseMongoDB ODM8.0.0
JWTAuthentication Tokens9.0.3
bcryptPassword Hashing6.0.0
corsCross-Origin Resource Sharing2.8.5
dotenvEnvironment Variables16.3.1

4. Database Models

4.1 User Model

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);
    

4.2 Device Model

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);
    
Note: The temperature and mode fields are only added when creating an AC device. Other devices (light, fan, tv, etc.) will not have these fields.

4.3 Log Model

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);
    

5. Authentication System

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.

5.1 Auth Middleware

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;
    
Note: The token should be sent directly in the Authorization header without the "Bearer" prefix.

6. API Endpoints

📋 Complete API URL List

#MethodURLDescriptionAuth Required
1GET/api/healthHealth checkNo
2POST/api/auth/registerRegister new userNo
3POST/api/auth/loginLogin & get tokenNo
4GET/api/devicesGet all devicesYes
5POST/api/devicesCreate new deviceYes
6PATCH/api/devices/:id/toggleToggle device on/offYes
7PATCH/api/devices/:id/ac-settingsUpdate AC temp & modeYes
8DELETE/api/devices/:idDelete a deviceYes
9GET/api/logsGet all activity logsYes
10GET/api/logs/:deviceIdGet logs for specific deviceYes

Base URL

https://smart-home-iot-mern-api.onrender.com

Example Full URLs

  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
    

6.1 Authentication Endpoints

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..." }

6.2 Device Endpoints

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" }

6.3 Log Endpoints

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": [...] }

7. Main Server File

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);
});
    

8. Complete Route Files

8.1 Authentication Routes

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;
    

8.2 Device Routes

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;
    

8.3 Log Routes

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;
    

9. Environment Configuration

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
    

10. How to Run

CommandDescription
npm installInstall all dependencies
npm run devRun in development mode with nodemon
npm startRun in production mode

Smart Home IoT Server Documentation

© 2025 Ashutosh Rai. All rights reserved.