DEV Community

Saheedat
Saheedat

Posted on

A Beginner's Guide to Using Mirage.js in a React TypeScript Smart Home Dashboard.

Hi everyone! I hit a wall while I was building the smart home dashboard, but I’m here now. Consistency is hard sometimes, but I guess what matters is showing up when it is needed.

The smart home dashboard system is complex and involves various devices that communicate through APIs. Step 4 in my roadmap to build the dashboard involves creating mock data to create certain functionalities for the app pending the time I find an actual API to use for it.

This is where Mirage.js comes in, offering a solution to mock backend services and simulate real-world data. Basically, it is helping me to build and test the frontend when the backend isn’t available. In this article, we will look at how to use Mirage.js to create a mock API for the smart home dashboard.

What Is Mirage.js and Why Do We Use It?

Mirage.js is a library that creates a mock server right in your browser. It can be thought of as a temporary stand-in for your real backend server. It's particularly useful when:

  • Your backend API isn't ready yet

  • You want to prototype features quickly

  • You need to test how your frontend handles different API responses

  • You're working offline

For my smart home dashboard, I needed to test different devices (lights, humidifiers, speakers, air conditioners etc) and how they would interact with the interface before connecting to real IoT devices' API. Here’s how I did it.

Setting Up Mirage.js

STEP 1: Install Mirage.js
First, you need to install mirage.js in your React TypeScript project.

npm install –save-dev miragejs

STEP 2: Create the basic server
Create a new file, and name it mirage.ts in your src directory. This is what the basic structure looks like:


import { createServer } from "miragejs";

export function makeServer({ environment = "development" } = {}) {
  return createServer({
    environment,

    models: {
      // We'll define our data models here
    },

    seeds(server) {
      // We'll add sample data here
    },

    routes() {
      // We'll define API endpoints here
    },
  });
}

Enter fullscreen mode Exit fullscreen mode

At this point, you need to have decided on the appliances that you want to mock and their properties such as id, type, room, status, etc.

For this project, some of the appliances to be used include light, humidifier, speaker, appliances (e.g TV, AC, heater).

STEP 3: Define typescript interfaces
Before we create our mock data, we have to define the types for our smart home devices:

interface Light {
  id: number;
  room: string;
  status: "on" | "off";
  brightness: number;
  color: string;
  schedule: string | null;
}

interface Humidifier {
  id: number;
  room: string;
  status: "on" | "off";
  schedule: string | null;
  energyEfficiency: boolean;
}

interface Appliance {
  id: number;
  type: "TV" | "Heater" | "AC";
  room: string;
  status: "on" | "off";
}
interface Speaker = {
  id: number;
  room: string;
  status: "playing" | "off";
  volume: number;
  energy: number;
};

interface AppSchema {
  light: Light;
  humidifier: Humidifier;
  appliance: Appliance;
  Speaker: Speaker
}

Enter fullscreen mode Exit fullscreen mode

STEP 4: Set up models and seed data
Here, you get to create our mock server with sample data:

import { createServer, Model, Server } from "miragejs";
import Schema from "miragejs/orm/schema";

export function makeServer({ environment = "development" } = {}): Server {
  return createServer({
    environment,

models: {
 light: Model.extend<Partial<Light>>({}),
 humidifier: Model.extend<Partial<Humidifier>>({}),
 appliances: Model.extend<Partial<Appliance>>({}),
 speaker: Model.extend<Partial<Speaker>>({}),
}

seeds(server){
//we are creating sample appliances
server.create("appliance", {
        id: 1,
        type: "TV",
        room: "Living Room",
        status: "off"
      });
      server.create("appliance", {
        id: 2,
        type: "Heater",
        room: "Bathroom",
        status: "off",
      });
      server.create("appliance", {
        id: 3,
        type: "Air Conditioner",
        room: "Bedroom",
        status: "on",
      });
//creating sample light
      server.create("light", {
        id: 1,
        room: "Living Room",
        status: "off",
        brightness: 50,
        color: "white",
        schedule: null,
      });

//creating sample humidifier

      server.create("humidifier", {
        id: 1,
        room: "Bathroom",
        status: "on",
        schedule: "7:00 AM",
        energyEfficiency: true,
      });
},

routes(){
this.namespace = “api”;

//the GET endpoints
    this.get("/lights", (schema: Schema<AppSchema>) => schema.all("light"));
        this.get("/humidifiers", (schema: Schema<AppSchema>) => schema.all("humidifier"));

        this.get("/appliances", (schema: Schema<AppSchema>) => schema.all("appliance"));
        this.get("/speakers", (schema: Schema<AppSchema>) => schema.all("speaker"));

//POST endpoints for toggling devices.
//In our dashboard, users can toggle some of the devices on and off

        this.post("/lights/:id/toggle", (schema: Schema<AppSchema>, request) => {
        const id = request.params.id;
        const light = schema.find("light", id);

        if (light) {
          const newStatus = light.status === "on" ? "off" : "on";
          return light.update({ status: newStatus });
        }
        return null;
      });
             },
     } );
}

Enter fullscreen mode Exit fullscreen mode

STEP 5: Starting the mirage server
To ensure that the server runs when the application is launched, you need to initialize Mirage.js in the main entry file i.e App.tsx

import {makeServer} from “./components/api/Mirage”;

if(import.meta.env.MODE === “development”){
makeServer();
}

Enter fullscreen mode Exit fullscreen mode

STEP 6: Using the Mock API in Components
With Mirage.js set up, we can now fetch data in the frontend just as you would with a real API.

Now, you can use your mock API in one of your components like this:

import { useEffect, useState } from 'react';
import { Light} from “./components/api/Mirage”;

interface Light {
  id: number;
  room: string;
  status: "on" | "off";
  brightness: number;
  color: string;
  schedule: string | null;
}

export const LightControls = () => {
  const [lights, setLights] = useState<Light[]>([]);

  useEffect(() => {
    fetch('/api/lights')
      .then(res => res.json())
      .then(data => setLights(data.lights));
  }, []);


  const toggleLight = async (id: string) => {
    const response = await fetch(`/api/lights/${id}/toggle`, {
      method: 'POST',
    });
    const updatedLight = await response.json();

    setLights(prevLights => 
      prevLights.map(light => 
        light.id === id ? updatedLight : light
      )
    );
  };

  return (
    <div className="grid gap-4 grid-cols-2 md:grid-cols-3">
      {lights.map(light => (
        <div key={light.id} className="p-4 border rounded-lg">
          <h3>{light.room} Light</h3>
          <p>Status: {light.status}</p>
          <button
            onClick={() => toggleLight(light.id)}
            className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
          >
            Switch
          </button>
        </div>
      ))}
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

Extra Tips

  1. Make sure your namespace in routes matches your API calls. It took me a while to figure that our because I forgot the /api prefix.

  2. Create seed data that resembles your real data as much as possible. It will make transitioning to the real API smoother.

  3. Start with the simplest functionality, then add complex ones gradually.

Conclusion

Mirage.js allowed me to build and test the frontend independently of the backend, speeding up the development process. In this smart home dashboard project, I used it to mock appliance data, allowing me to build out the device control interface without relying on a live backend API. This not only speeds up development but also ensures a smoother transition when the real API becomes available.

The next step in this project is to implement user authentication in the app.

See you soon!

Resources

  1. Mirage.js official documentation.

Top comments (0)