Data fetching in React the functional way powered by TypeScript, io-ts & fp-ts
Tags: typescript, react, functional
Published: 2019-01-20 20:38:00

---
title: Data fetching in React the functional way powered by TypeScript, io-ts & fp-ts
published: true
description: Using io-ts and fp-ts to perform type-safe data fetching with a declarative API
tags: typescript, react, fp
---

![](https://thepracticaldev.s3.amazonaws.com/i/wi21bxgi8149ukwcsngu.png)

Over the past few days, I've been working on a React application. It is a straightforward application that doesn't even require a database. However, I didn't want to embed all the content into the application's JSX because some of it will be updated frequently. So I decided to use a few simple JSON files to store the contents.

The application is the website for a conference, and I wanted to build a page that looks as follows:

![](https://thepracticaldev.s3.amazonaws.com/i/g84x1rm643k0pbt4juxd.png)

To generate a page like the one in the previous image I have stored the data in the following JSON file:

```js
[
    { "startTime": "08:00", "title": "Registration & Breakfast", "minuteCount": 60 },
    { "startTime": "09:00", "title": "Keynote", "minuteCount": 25 },
    { "startTime": "09:30", "title": "Talk 1 (TBA)", "minuteCount": 25 },
    { "startTime": "10:00", "title": "Talk 2 (TBA)", "minuteCount": 25 },
    { "startTime": "10:30", "title": "Talk 3 (TBA)", "minuteCount": 25 },
    { "startTime": "10:55", "title": "Coffee Break", "minuteCount": 15 },
    { "startTime": "11:10", "title": "Talk 4 (TBA)", "minuteCount": 25 },
    { "startTime": "11:40", "title": "Talk 5 (TBA)", "minuteCount": 25 },
    { "startTime": "12:10", "title": "Talk 6 (TBA)", "minuteCount": 25 },
    { "startTime": "12:35", "title": "Lunch, Networking & Group Pic", "minuteCount": 80 },
    { "startTime": "14:00", "title": "Talk 7 (TBA)", "minuteCount": 25 },
    { "startTime": "14:30", "title": "Talk 8 (TBA)", "minuteCount": 25 },
    { "startTime": "15:00", "title": "Talk 9 (TBA)", "minuteCount": 25 },
    { "startTime": "15:25", "title": "Coffee Break", "minuteCount": 15 },
    { "startTime": "15:40", "title": "Talk 10 (TBA)", "minuteCount": 25 },
    { "startTime": "16:10", "title": "Talk 11 (TBA)", "minuteCount": 25 },
    { "startTime": "16:40", "title": "Talk 12 (TBA)", "minuteCount": 25 },
    { "startTime": "17:10", "title": "Closing Remarks", "minuteCount": 25 }
]
```

## The problem

While using JSON files makes my life easier, data fetching in React is a very repetitive and tedious task. If that wasn't bad enough, the data contained in an HTTP response could be completely different from what we are expecting.

The type-unsafe nature of fetch calls is particularly dangerous for TypeScript users because it compromises many of the benefits of TypeScript. So I decided to experiment a little bit to try to come up with a nice automated solution.

I have been learning a lot about functional programming and Category Theory over the past few months because I've been writing a book titled [***Hands-On Functional Programming with TypeScript***](http://www.functionaltypescript.com/).

I'm not going to get too much into Category Theory in this blog post. However, I need to explain the basics. Category Theory defines some types that are particularly useful when dealing with side effects.

The Category Theory types allow us to express potential problems using the type system and are beneficial because they force our code to handle side effects correctly at compilation time. For example, the `Either` type can be used to express that a type can be either a type `Left` or another type `Right`. The `Either` type can be useful when we want to express that something can go wrong. For example, a `fetch` call can return either an error (left) or some data (right).

### A) Ensure that errors are handled

I wanted to make sure that the return of my `fetch` calls are an `Either` instance to ensure that we don't try to access the data without first guaranteeing that the response is not an error.

I'm lucky because I don't have to implement the `Either` type. Instead, I can simply use the implementation included in the [fp-ts](https://github.com/gcanti/fp-ts) open source module. The `Either` type is defined by fp-ts as follows:


```ts
declare type Either<L, A> = Left<L, A> | Right<L, A>;
```

### B) Ensure that data is validated

The second problem that I wanted to solve is that even when the request returns some data, its format could be not what the application is expecting. I needed some runtime validation mechanism to validate the schema of the response. I'm lucky once more because instead of implementing a runtime validation mechanism from scratch, I can use another open source library: [io-ts](https://github.com/gcanti/io-ts).

## The solution

> TL;DR This section explains the implementation details of the solution. Feel free to skip this part and jump into "The result" section if you are only interested in the final consumer API.

The io-ts module allows us to declare a schema that can be used to perform validation at runtime. We can also use io-ts to generate types from a given schema. Both of these features are showcased in the following code snippet:


```ts
import * as io from "io-ts";

export const ActivityValidator = io.type({
    startTime: io.string,
    title: io.string,
    minuteCount: io.number
});

export const ActivityArrayValidator = io.array(ActivityValidator);

export type IActivity = io.TypeOf<typeof ActivityValidator>;
export type IActivityArray = io.TypeOf<typeof ActivityArrayValidator>;
```

We can use the `decode` method to validate that some data adheres to a schema. The validation result returned by `decode` is an `Either` instance, which means that we will either get a validation error (left) or some valid data (right).

My first step was to wrap the `fetch`  API, so it uses both fp-ts and io-ts to ensure that the response is an `Either` that represents an error (left) or some valid data (right). By doing this, the promise returned by`fetch` is never rejected. Instead, it is always resolved as an `Either` instance:

```ts
import { Either, Left, Right } from "fp-ts/lib/Either";
import { Type, Errors} from "io-ts";
import { reporter } from "io-ts-reporters";

export async function fetchJson<T, O, I>(
    url: string,
    validator: Type<T, O, I>,
    init?: RequestInit
): Promise<Either<Error, T>> {
    try {
        const response = await fetch(url, init);
        const json: I = await response.json();
        const result = validator.decode(json);
        return result.fold<Either<Error, T>>(
            (errors: Errors) => {
                const messages = reporter(result);
                return new Left<Error, T>(new Error(messages.join("\n")));
            },
            (value: T) => {
                return new Right<Error, T>(value);
            }
        );
    } catch (err) {
        return Promise.resolve(new Left<Error, T>(err));
    }
}
```

Then I created a React component named `Remote` that takes an `Either` instance as one of its properties together with some rendering functions. The data can be either `null | Error` or some value of type `T`.  

The `loading` function is invoked when the data is `null`, the `error` is invoked when the data is an `Error` and the `success` function is invoked when data is a value of type `T`:

```ts
import React from "react";
import { Either } from "fp-ts/lib/either";

interface RemoteProps<T> {
  data: Either<Error | null, T>;
  loading: () => JSX.Element,
  error: (error: Error) => JSX.Element,
  success: (data: T) => JSX.Element
}

interface RemoteState {}

export class Remote<T> extends React.Component<RemoteProps<T>, RemoteState> {

  public render() {
    return (
      <React.Fragment>
      {
        this.props.data.bimap(
          l => {
            if (l === null) {
              return this.props.loading();
            } else {
              return this.props.error(l);
            }
          },
          r => {
            return this.props.success(r);
          }
        ).value
      }
      </React.Fragment>
    );
  }

}

export default Remote;
```

The above component is used to render an `Either` instance, but it doesn't perform any data fetching operations. Instead, I implemented a second component named `Fetchable` which takes an `url` and a `validator` together with some optional `RequestInit` configuration and some rendering functions. The component uses the `fetch` wrapper and the `validator` to fetch some data and validate it. It then passes the resulting `Either` instance to the `Remote` component:

```ts
import { Type } from "io-ts";
import React from "react";
import { Either, Left } from "fp-ts/lib/Either";
import { fetchJson } from "./client";
import { Remote } from "./remote";

interface FetchableProps<T, O, I> {
    url: string;
    init?: RequestInit,
    validator: Type<T, O, I>
    loading: () => JSX.Element,
    error: (error: Error) => JSX.Element,
    success: (data: T) => JSX.Element
}

interface FetchableState<T> {
    data: Either<Error | null, T>;
}

export class Fetchable<T, O, I> extends React.Component<FetchableProps<T, O, I>, FetchableState<T>> {

    public constructor(props: FetchableProps<T, O, I>) {
        super(props);
        this.state = {
            data: new Left<null, T>(null)
        }
    }

    public componentDidMount() {
        (async () => {
            const result = await fetchJson(
                this.props.url,
                this.props.validator,
                this.props.init
            );
            this.setState({
                data: result
            });
        })();
    }

    public render() {
        return (
            <Remote<T>
                loading={this.props.loading}
                error={this.props.error}
                data={this.state.data}
                success={this.props.success}
            />
        );
    }

}

```

## The result

I have released all the preceding source code as a module named [react-fetchable](https://github.com/remojansen/react-fetchable). You can install the module using the following command:

```sh
npm install io-ts fp-ts react-fetchable
```

You can then import the `Fetchable` component as follows:

```ts
import { Fetchable } from "react-fetchable";
```

At this point I can implement the page that I described at the beguinning:

```ts
import React from "react";
import Container from "../../components/container/container";
import Section from "../../components/section/section";
import Table from "../../components/table/table";
import { IActivityArray, ActivityArrayValidator } from "../../lib/domain/types";
import { Fetchable } from "react-fetchable";

interface ScheduleProps {}

interface ScheduleState {}

class Schedule extends React.Component<ScheduleProps, ScheduleState> {
  public render() {
    return (
      <Container>
        <Section title="Schedule">
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit,
            sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          </p>
          <Fetchable
            url="/data/schedule.json"
            validator={ActivityArrayValidator}
            loading={() => <div>Loading...</div>}
            error={(e: Error) => <div>Error: {e.message}</div>}
            success={(data: IActivityArray) => {
              return (
                <Table
                  headers={["Time", "Activity"]}
                  rows={data.map(a => [`${a.startTime}`, a.title])}
                />
              );
            }}
          />
        </Section>
      </Container>
    );
  }
}

export default Schedule;
```

I can pass the URL `/data/schedule.json` to the `Fetchable` component together with a validator `ActivityArrayValidator`. The component will then:

1. Render `Loading...`
2. Fetch the data
3. Render a table if the data is valid
4. Render an error is the data cannot be loaded doesn't adhere to the validator

I'm happy with this solution because it is type-safe, declarative and it only takes a few seconds to get it up and running. I hope you have found this post interesting and that you try [`react-fetchable`](https://github.com/remojansen/react-fetchable).

Also, if you are interested in Functional Programming or TypeScript, please check out my upcoming book [***Hands-On Functional Programming with TypeScript***](http://www.functionaltypescript.com/).

[![](https://thepracticaldev.s3.amazonaws.com/i/n5sfa1euspqvdye5cky6.png)](http://www.functionaltypescript.com/)