Faster Iteration with Mock APIs
Tags: api, mocking, javascript, typescript
Published: 2020-02-06 22:28:09

---
title: "Faster Iteration with Mock APIs"
published: true
description: We frequently work on front-ends along side other teams building APIs. Sometimes those APIs are not yet available. But deadlines and sprint demos are approaching. Are we to sit around twiddling our thumbs? Nope!
tags: #apis, #mocking, #javascript, #typescript
canonical_url: https://www.olioapps.com/blog/mock-apis/
---

We frequently work on front-ends along side other teams building APIs. Sometimes those APIs are not yet available. But deadlines and sprint demos are approaching. Are we to sit around twiddling our thumbs? Nope!

The approach we take is to get the API spec if available, or if not, imagine how we would want this API to look and behave.

Then we create mock API functions _within_ our front-end application that do not make any requests, but behave exactly as if calling the real API, returning fake data.

An alternative is to use a hosted fake API server like [json-server](https://github.com/typicode/json-server). In some cases, that might be a better choice - such as if the front-end is deployed to a staging site, and you want to be able to edit the fake responses without redeploying.

During the development phase, we prefer to use mocking functions instead, so we don't increase our dependencies or have to rely on an external service for testing.

This allows us to decouple our development process from that of other teams we are relying on, and quickly put together working demos that illustrate how the application will perform once the API is online.

## Example API - a list of tips

Suppose our backend API will return a set of "tips". A tip is a short piece of content, to show to a user on start up. Think _Clippy_ or, a "Did you know? ..." pop-up. The front end application will fetch these tips from the API and show them to the user.

We have mockups, but the API is still under development. Fortunately, the shape and contract of the API is known. If we build the UI to be data driven now, we won't have to go back and do partial rework later. When the API comes online, we can flip a switch and it should "just work".

## API response shape

This API will exist on an authenticated HTTPS endpoint. Let's say its `GET https://special.api/tips`. According to the spec, the response JSON shape will be:

```json
{
  "tips": [
    {
      "id": 1,
      "type": "info",
      "title": "Set a daily or weekly reminder to track your time",
      "subtitle": "Tracking",
      "description": "Over time, we can provide insights and suggestions for improvement",
      "link": "https://url/to/set-reminder",
      "label": "Add Reminder"
    },
    // ... more tips
  ]
}
```

## Static Typing

At Olio Apps we've been enjoying [TypeScript](https://www.typescriptlang.org/) lately, which allows us to create static types for things like this json structure:

```typescript
// the tip itself
interface ApiTip {
  readonly id: number
  readonly type: string
  readonly title: string
  readonly subtitle: string
  readonly description: string
  readonly link: string
  readonly label: string
}

// the api response envelope, an object that has an array of tips
interface ApiTips {
  readonly tips: ReadonlyArray<ApiTip>
}
```

## Immutable Data

Note the use of `readonly` and `ReadonlyArray<T>` which enforce immutability. We use [tslint-immutable](https://github.com/jonaskello/tslint-immutable) to ensure all data types are immutable and checked at compile time, instead of at run time with something like [Immutable.js](https://github.com/facebook/immutable-js/). This yields higher performance and more straightforward code.

## API Client

The following function makes a request to fetch tips:

```typescript
export function getTips(): Promise<HandledResp> {
  if (useMock) {
    return fakeGetTips().then((res: Resp) => handleResp(res))
  }

  return request
    .get(buildUrl("/tips"))
    .set(authHeader())
    .type("application/json")
    .accept("application/json")
    .then((res: Resp) => handleResp(res))
    .catch((res: Resp) => handleResp(res))
  }
}
```

## The Mock API Function

Note the `useMock` check. If this configuration variable is `true`, we'll call `fakeGetTips()` instead of making a request.

Here's what `fakeGetTips()` looks like:

```typescript
export function fakeGetTips(): Promise<Resp> {
  const payload: ApiTips = {
    tips: [
      {
        "id": 1,
        "type": "info",
        "title": "Set a daily or weekly reminder to track your time",
        "subtitle": "Tracking",
        "description": "Over time, we can provide insights and suggestions for improvement",
        "link": "https://url/to/set-reminder",
        "label": "Add Reminder"
      },
      // more fake tips
    ],
  }

  return Promise.resolve({
    text: JSON.stringify(payload, null, 4),
    status: 200,
    statusText: "",
  })
}
```

Just like a standard request, `fakeGetTips()` return a promise that resolves to a standard response object:

```typescript
interface Resp {
  readonly text: string
  readonly status: number
  readonly statusText: string
}
```

## Standard Response Handling

In both versions, when the response value comes back in a promise, we handle it with `.then((res: Resp) => handleResp(res))`.

The `handleResp` function converts the response object into a normalized structure, where any text from the response is parsed as json and returned as `items`, along with the `status` and `statusText`.

```typescript
const handleResp = (resp: Resp): HandledResp => {
    // if text present, parse as json, otherwise empty array
    const items: {} = resp.text ? JSON.parse(resp.text) : []
    // return normalized response object
    return ({
        items,
        status: resp.status,
        statusText: resp.statusText,
    })
}

interface HandledResp {
    readonly items: {} // can be any javascript type
    readonly status: number
    readonly statusText: string
}
```

All downstream functions can now rely on `items` to be the response data, converted back into a javascript object. Once the `tips` API comes online, we can simply change `useMock` to false, and it should work as long as the API data structure hasn't changed.