Custom Routes With Mirage JS

Samuel Guo
The Startup
Published in
8 min readNov 17, 2019

Background

In the last ReactNYC Meetup, (yes, the same one that I referenced to in my previous blog), a new technology was introduced to me: Mirage JS.

Mirage JS is an API mocking library that lets you build, test and share a complete working JavaScript application without having to rely on any backend services.

Mirage JS accomplishes this by creating a mock server and intercepts any network requests to the mock server in lieu of an official server. It creates the mock server within the browser. Sam Selikoff gave an introductory presentation on how to jump start your React application with a mock server via Mirage JS. He presented Mirage JS by using RESTful conventions for the mock server and a mock database to accompany those conventions.

I researched a bit more into Mirage JS and took a look at the Github page the site used for their demonstration. Again, it was all RESTful routes and conventions, similar to Sam’s presentation. That’s when I decided to experiment around with Mirage JS and see if it can go beyond what I’ve read and seen thus far.

Local Storage with Mirage JS

The opposite of RESTful routes would be custom routes so the first idea of custom routes that came to mind was authentication via local storage. Since Mirage JS creates a mock server, the MVC (model-view-controller) pattern can still be implemented. I will be skipping the model portion for my local storage example as the model will only help to serve to confirm the decoded local storage payload against the user model (which can be done via schema.users.findBy({ ATTRIBUTE TO FIND BY }) .

For the local storage example, I shall be using JWT (JSON Web Token) that is encoded and stored via local storage. If you are not familiar with JWT, I highly recommend reading about it on their site to familiarize yourself with it before reading this blog further.

Note: Please do not refer to this blog as a reference for using JWT for your security implementations. This blog is merely to demonstrate that Mirage JS can also be used for custom routes.

Now that the idea is out there, lets implement it!

Installing Mirage JS and JWT

Before we can start using the tools, we will need the tools themselves. I followed Mirage JS’s quick start guide for installation purposes and JWT’s documentation. The two commands below will install these packages.

For Mirage JS:

npm install --save-dev miragejs

For JWT:

npm install --save jsonwebtoken

Mirage JS Mock Server

Now that we have our tools, let’s start implementing them. Below is my initial mock server setup:

Initial Mock Server

This mock server is fairly bare-boned. All the function does is initiate a new server (line 4) in the development environment (line 3) and creates a name space for my routes, meaning that each route that my mock server tries to ping need to all start with “/api”. This is similar to the server shown in the quick guide except I deleted the seeds, models, and user route since I am not using any models.

Additional code shall be added as needed throughout this blog. Let’s jump to the front-end.

App.js

The gist below is the starting point on where the code get more interesting.

Initial App

In this gist, I am using the React hook useEffect() to immediately start a fetch request as soon as the component is mounted.

Normally for authentication, as soon as the user provides some sort of credential and it is actually authenticated, then a JWT would be issued. In this example, I am skipping those steps and immediately issuing a JWT as soon as the component is mounted.

In my fetch request, I am making a GET request to the route “/api/auth”, which I currently do not have in the mock server. So… lets build it out!

Creating and Decoding JWT

Let’s include the “/api/auth” route into the mock server.

Initial Auth Route

In Mirage JS, this.get, this.post, this.patch, and this.del are typically the syntax for the mock routes. This is based on the HTTP verbs which should be straightforward to remember.

According to the Mirage JS documentation,

Each verb method has the same signature. The first argument is the path (URL) and the second is the actual function handler that returns the response.

I know that I want to create an encoded JWT in this case, so the body of the function handler needs to accomplish this.

By looking at the JWT documentation, I can do this by using the sign method.

Completed Auth Route

In lines 12–14, I created a new JWT where the payload is { message: "YOU'VE DECODED ME!" } with the secretCode . The secretCode is the generated code from the JWT website in which I saved it in a separate file called secret.js . Now I have a response from the fetch request.

Let’s save the encoded token into the local storage and change the state to display the status of the token.

Completed Auth Fetch Request

In lines 8–14, I set an item in the local storage with the key-value pair of token and the encrypted code. Then I set the state of the token to show in the browser that the message is still encoded. Below is a screenshot on what this looks like.

If you’re wondering why I had to wrap the setting of a web token in an if statement, I will explain it later in this blog.

Creating the Encoded Token

Great! Now that we’ve created the encoded JWT, let’s decode it. To trigger this event, I am going to add a button that would trigger the decoding function and the route that it will ping to.

Initial Decode Function

I added the button via line 29 and created the decoding function in lines 17–24. Now since I don’t have a decode route yet, let’s create one, just like how we did it for the auth route. I am also sending the encoded token from the local storage via headers in the fetch request so that I can send it to my mock server (or else how I am supposed to decode it otherwise?).

Initial Decode Route

Hmmm, now I need to decode the encoded token. But in order to do that, I need to retrieve the encoded token from the headers.

According to the Mirage JS documentation:

The second parameter is the request object, which contains information about the request your app made. For example, you can access dynamic URL segments from it:

After painstakingly browsing all of the key-value pairs and a handful of Google searches, the way to retrieve the encoded token is request.requestHeaders.authorization .

Sweet, now that I have the encoded token, I need to decode it. Thankfully, the JWT documentation is straightforward and I can use the verify method to decode it with the same secretCode I used to encode it.

Completed Decode Route

Lines 18–22 is the function handler that allows me to decode the encoded token and return the decoded token. However, in line 18, I had to fill in the first argument with schema and then the second argument with request, the argument of interest. Since we are not using the first argument in this example, I attempted to replace it with null but was getting errors. Per Mirage JS conventions, I named the first argument schema to circumvent said errors.

Now that we are returning data from the decode route, let’s complete the decode function.

Completed Decode Function

In line 24–28, I received the decoded token from the mock server and then set the state of the token to the decoded token to display its hidden message. As a quick sanity check, I also console logged the data to ensure that the initial payload was the same as before. For the key iat :

Generated jwts will include an iat (issued at) claim by default unless noTimestamp is specified.

Since the key iat is auto generated and I am not manipulating that data key-value pair, I can safely ignore it for this example.

Decoded Token

useEffect()

Recall earlier how I mentioned that I had to use an if statement in the useEffect() hook? This is due to the implicit nature of this hook. According to the React hooks documentation:

Tip

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

Essentially, the way I am using useEffect(), I am doubling it up as componentDidMount and componentDidUpdate. The componentDidMount aspect is the auth route fetch request whereas the componentDidUpdate aspect is the decode route fetch request.

Without the if statement, the following below would be the state logic flow of token:

  1. token: “ENCODED SECRET MESSAGE” (when the component mounted)
  2. Click on the button to decode the encoded token
  3. token: “YOU’VE DECODED ME” (from when the token updated within the decode function)
  4. token: “ENCODED SECRET MESSAGE” (in the useEffect hook since the token state was updated)

I placed some console.logs to showcase this flow and the screenshot below proves that.

useEffect doubling up as componentDidMount and componentDidUpdate

By adding the if statement, its prevents the componentDidUpdate aspect from updating the token state and resetting it back to “ENCODED SECRET MESSAGE”. Below is the a screenshot with the console.logs to showcase this.

Prevented useEffect from doubling up

However, you will notice that after the decode function in the console, the browser still makes another GET request to the auth route but does not update the token state. This is because when the token state is updated in the decode function, that triggered the componentDidUpdate aspect, which triggers the useEffect() hook. Due to the location of the if statement in the code, it does not prevent the code from accessing the route although it does prevent changing the state of the token. This means whenever the button is pressed, the code will access the auth route and create a new JWT every single time. This is horrendous practice and should never be implemented in this way. Since this blog is about custom routes, we can skip over this issue for the time being.

Key Takeaways

While tinkering around with Mirage JS and creating this example, I learned how powerful Mirage JS can be. Below were my discoveries:

  1. The function handlers in the mock server routes, such as this.gets(), is essentially the same as a controller in the MVC pattern. How the function handlers handle the data within each of their respective routes is analogous to a Controller class in Ruby on Rails.
  2. The useEffect() hook combines the functionalities of componentDidMount, componentDidUpdate, and componentWillUnmount. Differentiating these two functionalities within the same hook may prove difficult (potentially research and read more in-depth the React hooks documentation).
  3. This discovery, along with my first one, was what made me understand how powerful Mirage JS can be. As a front-end/full stack developer, Mirage JS can mock up your entire database and back-end architecture before developing the back-end itself. Based on using a MVC pattern, the models are in the mock server, the RESTful and custom routes can be created/tested, the controllers are in the function handlers, and the views are in the front-end code itself. The major things left to do would be to transfer the Mirage JS portion of your code from the Javascript language to the back-end language of your choice, disconnect the front-end code from the mock server, and connect the front-code to the actual server. And then, BAM, you have a fully functioning application ready for production!

--

--

Samuel Guo
The Startup

Full stack software developer with experience in Javascript, React, Redux, and Ruby on Rails and a background in Mechanical Engineering.