Mirage JS: build mock APIs for your frontend app

Mirage JS: build mock APIs for your frontend app

Featured on Hashnode

You have your frontend design all sketched up and ready.
You can't wait to code that thing up!
Everything's going as planned.

And suddenly the backend engineer tells you that the API is not yet ready and will take some time. Bam!

Your ideas flowing through your finger tips to your keyboard then to the editor then to the browser... all stopped.

Well this is exactly where Mirage JS comes to the rescue. It allows you to quickly build mock APIs for your frontend app. And the best part is that it runs in your browser only. You don't need a server for this.

So, lets get started.

We will be building a simple API to get a list of playlists from server and post new playlists.

I will be using React(because I love it) but you can use any other library or framework for your frontend. It will work the same.

I have used codesandbox. You can find the link at the end.

Creating the API

Create a file named mock-server.js and paste the following code:

import { createServer, Model, RestSerializer } from "miragejs";

export const mockServer = () => {
  createServer({
    serializers: {
      application: RestSerializer
    },

    models: {
      playList: Model
    },

    routes() {
      this.timing = 1500;

      this.get("/api/play-list", (schema, request) => {
        return schema.playLists.all();
      });

      this.post("/api/play-list", (schema, request) => {
        let newPlayList = JSON.parse(request.requestBody).playList;
        return schema.playLists.create(newPlayList);
      });
    },

    seeds(server) {
      server.create("playList", {
        name: "Upbeat"
      });

      server.create("playList", {
        name: "Rock"
      });
    }
  });
};

Well that's a lot. Lets take a few minutes to understand what's happening here.

import { createServer, Model, RestSerializer } from "miragejs";

In the first line, we are importing three things from the mirage package.

  • createServer is used to create a new server.
  • Model is used to define a new model. Think of this like your database.
  • RestSerializer is used to transform your model to JSON like format.

Next we are exporting mockServer function which we will call from our App.jsx file. Inside this function, we are using the createServer to create our server instance. Then we are setting the serializer to RestSerializer.

After this, we are defining a model named playList. We will use this to store our playlist names.

Now, lets come to the main part of this: routes.
Here, we are defining a route which will be our API, which is "/api/play-list". There are two methods defined here: get and post. We will be getting all the data via get and posting our new playlist name via post.

And at the end, we have used seeds(server) to seed the data (create the models). Here I have created two playlists: Upbeat and Rock. With that, our API is setup and now we need to consume it.

Consuming the API

Let's look at the frontend React code now.

We will import our exported mockServer function here in the App.jsx:

import { mockServer } from "./api/mock-server";

And we start the mock API server like this:

mockServer();

Next, we have a function to get the data from the server, i.e., from the API. We are fetching the API via axios:

const getDataOnClick = async () => {
    try {
      setIsLoading(true);
      const res = await axios.get("/api/play-list");
      setPlayList(res.data.playLists);
    } catch (error) {
      console.log(error);
    } finally {
      setIsLoading(false);
    }
  }

And when we get the data from the server, we update the state accordingly.

We also need to post the data via API:

const addNewPlayListOnClick = async () => {
    try {
      setIsLoading(true);
      const res = await axios.post("/api/play-list", {
        playList: {
          name: inputText
        }
      });
      if (res.status === 201) {
        setPlayList((prev) => [...prev, res.data.playList]);
      }
      setInputText("");
    } catch (error) {
      console.log(error);
    } finally {
      setIsLoading(false);
    }
  };

Here, we are doing a post request to the API and we are sending an object to the API which has a name as the property.
Note that we are not sending the id here, it is generated by mirage automatically. We are also updating the state once we get the 201 response status from the server.

Also, I have added some UX hints like loading indicator and we are just console logging the error.

That's it with the logic part. Lets quickly checkout the view:

return (
    <div className="App">
      <h1>Mirage JS: build mock APIs</h1>

      <div>
        <input
          type="text"
          placeholder="Enter new playlist:"
          value={inputText}
          onChange={(e) => setInputText(e.target.value)}
        />
        <button onClick={addNewPlayListOnClick}>Add</button>
      </div>

      <button onClick={getDataOnClick}>Get all playlists</button>

      <h4>Your playlists:</h4>
      <ul>
        {playList.map((list) => (
          <li key={list.id}>
            {list.id}. {list.name}
          </li>
        ))}
      </ul>
      {isLoading && <h3>Loading...</h3>}
    </div>
  );

This is what our view looks like. We have an input box to get the data from the user and then we have two buttons: Add and Get all playlists. The Add button calls the addNewPlayListOnClick function and the Get all playlists button calls the getDataOnClick function.

And that's it.

The user types the name of new playlist and clicks on the Add button to post the data to the server. If the user clicks on Get all playlists button, the get request is called and the list is updated in real time.

Checkout the codesandbox for the source code and play around with it.

Also, do checkout the mirage docs .

Thank you for reading. This is my first tutorial blog post on hashnode.
Please share your feedback.
And yes, just reading will not do anything, you need to work on it. So, go on explore mirage and build cool projects!

There is a lot to mirage other than this. As we know:

“A journey, towards a mirage, never ends.”