close icon
daily.dev platform

Discover more from daily.dev

Personalized news feed, dev communities and search, much better than what’s out there. Maybe ;)

Start reading - Free forever
Continue reading >

Introduction to Mirage.js: Mocking APIs

Introduction to Mirage.js: Mocking APIs
Author
Chidume Nnamdi
Related tags on daily.dev
toc
Table of contents
arrow-down

🎯

Frontend development is always closely followed by a server the frontend will communicate with for data to render. But, sometimes we need to focus on the frontend part first before working on the backend, but we need data from the backend to test out how our frontend will render them.

Whipping out a real server may hinder our progress and it will be a painstaking task. So we need a way to set up a “fake” server to serve as if in prod without actually setting up a real server.

Today, we will be looking at a JS library that gives us the super-power to create instant powerful “fake” APIs, it is Mirage.js.

Mocking APIs

Many libraries have been developed for mocking APIs in JS. We have JSON-server, JSON-server-graphql, Mockoon. etc.

Mocking API is the process of hooking into an API and providing a custom service that runs instead of the real API whenever the API is called. This is very useful especially when we are testing our code.

Also in JS frameworks like Angular, it has an in-built method on how we can mock HTTP calls. In Angular, we use the HttpInterceptor for mocking a backend.

HTTP libraries like axios have a feature that we can use to mock HTTP requests. Axios has interceptors that can be used to intercept requests and send them back with our response if we want to mock an API.

Mirage.js

Mirage JS is an API mocking library that lets you build, test and share a complete working JavaScript application. Mirage.js is a client-side mocking framework. This means that it mocks APIs only in the browser.

How does it work?

Mirage.js utilizes the Prerender library to hook into the native XMLHttpRequest and fetch function. So with each call to any of them (an HTTP request), Mirage.js intercepts it without making an actual HTTP request to a server.

With Mirage.js, we can set the routes we want to mock, and also the response to return and the HTTP methods to mock.

In the next sections, we will learn how to install Mirage.js and how to use it.

Installation

Mirage.js is very easy to install and use in your project. It can be in your React, Angular, Vue, vanilla apps.

To install Mirage.js, run the command:


    npm i miragejs -D

This installs the Mirage.js library as a dev dependency, the -D makes sure of that. Installing it as a dev dependency excludes it from the production bundle. Yes, in production you use the real server, Mirage.js is used in the development stage and when testing.

If you are using yarn, run the command:


    yarn add miragejs -D

Now, we are set to go.

Mocking

To start mocking HTTP APIs, we need to import the createServer function from Mirage.js.


    import { createServer } from "miragejs"

Then, we call the createServer function.


    createServer()

That’s it.

Mirage.js will start intercepting HTTP request in our app, but since we have not specified the routes to mock, Mirage.js will let the HTTP request and will only log:


```Mirage: Your app tried to HTTP_METHOD_HERE 'YOUR_API_HERE', but there was no route defined to handle this request.```

We will use the routes() hook to set the API routes we want to mock.

All we have to do is set a function in the createServer function.


    createServer({
        routes() {
            // ...
        }
    })

Now we set our routes in it. Mirage.js supports a wide range of HTTP methods for mocking. To mock a GET HTTP request to “/api/posts” API.


    createServer({
        routes() {
            this.get("/api/posts", () => {
                return {
                    posts: [
                        "This is my FB post",
                        "Setting up my Twitter"
                    ]
                }
            })
        }
    })

The this.get() call is provided by Mirage.js. This mocks the API endpoint in it. It has two parameters, the first parameter is the endpoint we want to mock, the second parameter is the callback function. This function returns data that will be returned when the API endpoint is called.

Example:


    fetch({
        url: "/api/posts",
        method: "GET"
    }).then((response) => {
        console.log(response)
            /*
                posts: [
                    "This is my FB post",
                    "Setting up my Twitter"
                ]        
            */
    })

We used the fetch() API to make a GET request to the "api/posts" endpoint. This was mocked in Mirage.js, so it returns the array we set. That is why the response of the API call logs the array data.

For a POST endpoint, we will use the this.post(...) function in the routes() hook.


    createServer({
        routes() {
            // ...
            this.post("/api/posts/create", () => {
                return {
                    status: true,
                    msg: "New post successfully published."
                }
            })
        }
    })

This mocks an HTTP POST “/api/posts/create” endpoint. It returns an object, with a status property that states the success of the post and a msg that gives you a description of the success.

If we call the endpoint we will see this:


    fetch({
        url: "/api/posts/create",
        method: "POST"
    }).then((response) => {
        console.log(response)
            /*
                {
                    status: true,
                    msg: "New post successfully published."
                }
            */
    })

We can use other HTTP methods:


    this.patch('/api/posts/editPost', (schema, request) => {

    });

    this.put('/api/posts/updatePost', (schema, request) => {

    });

    this.del('/api/posts/deletePost', (schema, request) => {

    });

    this.options('/api/posts/', (schema, request) => {

    });

We haven’t talked about this, but because we just added them. Let’s learn what they are. I’m talking about the schema and request args in the callback functions in the HTTP method functions.

The schema arg is the primary way you interact with Mirage's data layer.

schema.db gives us access to the Mirage.js in-memory database so we can manipulate the database.

schema.db.loadData(...) is used to seed the database with data.

The request arg holds properties based on the request sent.

request.requestBody holds the body of a POST request.

request.params holds the parameters in the endpoint URL.

Seeds

Right now, our response data is hardcoded in our mocked API. We can make it more dynamic, to do that we will use the seeds() hook.


    createServer({
            seeds(server) {
                // ...
            }
        }
    })

Now, we will write the code to seed our database with data. The seeds function property accepts an arg (I named mine server) that contains methods and properties used to access the database.


    createServer({
            seeds(server) {
                server.create("posts", "Setting up my twitter")
                server.create("posts", "I'm hungry.")
            }
        }
    })

The server.create(...) method creates new posts data.

Now, we can use schema.db.posts to get the posts data in the callback function:


    createServer({
        // ...
        routes() {
            this.get("/api/posts", (schema) => {
                return schema.db.posts
            })
        }
    })

Dynamic routes

Mirage.js supports dynamic routes.

Dynamic routes are routes that its segments can change, it is especially used to perform a CRUD action to a particular resource in the server.

For example, we can have a generic route a get a particular post from our server:


    api/posts/:id

The :id in the URL represents a post id in the server. So it can be:


    api/posts/1
    api/posts/2

See the URLs are the same as the generic api/posts/:id. Dynamic routes are set by using the colon followed immediately by the name of the segment.


    createServer({
        // ...
        routes() {
            this.get("/api/posts/:id", (schema, request) => {
                const postId = request.params.id
                return schema.db.posts.get(postId)
            })
        }
    })

The request.params is an object that contains the dynamic segments in a URL in a key-value pair. So that's why we referred to the passed in id via the id property. It will be the value of :id of the HTTP request.

If the HTTP request is this /api/posts/1, then request.params.id will be 1.

Next, we use the schema.db.posts.get(...) to retrieve the post from the posts store using the post id, and then return it to the HTTP request as a response.

Conclusion

There is no doubt that Mirage.js is powerful, advanced, and awesome. I personally like Mirage.js because there is no need to spin up a server before setting up mocking routes, you just write the routes in your frontend code, and voila there you go!!

If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email, or DM me.

Mirage.js REPL

You can play with Mirage.js online without installing any libraries or setting up a front-end project.

The online Mirage.js REPL is at:
REPL Build, test and demo your JavaScript application without an API miragejs.com

References

Why not level up your reading with

Stay up-to-date with the latest developer news every time you open a new tab.

Read more