Tuesday, March 21, 2023
HomeSoftware EngineeringInformation fetching in React the useful manner powered by TypeScript, io-ts &...

Information fetching in React the useful manner powered by TypeScript, io-ts & fp-ts

Over the previous few days, I’ve been engaged on a React utility. It’s a simple utility that doesn’t even require a database. Nonetheless, I didn’t need to embed all of the content material into the appliance’s JSX as a result of a few of it will likely be up to date steadily. So I made a decision to make use of just a few easy JSON recordsdata to retailer the contents.

The appliance is the web site for a convention, and I needed to construct a web page that appears as follows:

To generate a web page just like the one within the earlier picture I’ve saved the info within the following JSON file:

    { "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 issue #

Whereas utilizing JSON recordsdata makes my life simpler, knowledge fetching in React is a really repetitive and tedious activity. If that wasn’t unhealthy sufficient, the info contained in an HTTP response might be utterly completely different from what we predict.

The kind-unsafe nature of fetch calls is especially harmful for TypeScript customers as a result of it compromises most of the advantages of TypeScript. So I made a decision to experiment a bit of bit to attempt to provide you with a pleasant automated resolution.

I’ve been studying so much about useful programming and Class Idea over the previous few months as a result of I’ve been writing a e book titled Palms-On Practical Programming with TypeScript.

I’m not going to get an excessive amount of into Class Idea on this weblog submit. Nonetheless, I would like to elucidate the fundamentals. Class Idea defines some varieties which are significantly helpful when coping with unwanted effects.

The Class Idea varieties enable us to specific potential issues utilizing the sort system and are useful as a result of they pressure our code to deal with unwanted effects appropriately at compilation time. For instance, the Both sort can be utilized to specific {that a} sort will be both a kind Left or one other sort Proper. The Both sort will be helpful after we need to specific that one thing can go improper. For instance, a fetch name can return both an error (left) or some knowledge (proper).

A) Be certain that errors are dealt with #

I needed to guarantee that the return of my fetch calls are an Both occasion to make sure that we don’t attempt to entry the info with out first guaranteeing that the response shouldn’t be an error.

I’m fortunate as a result of I don’t should implement the Both sort. As an alternative I can merely use the implementation embody within the [fp-ts](https://github.com/gcanti/fp-ts) open supply module. The Both sort is outlined by fp-ts as follows:

declare sort Both<L, A> = Left<L, A> | Proper<L, A>;

B) Be certain that knowledge is validated #

The second drawback that I needed to unravel is that even when the request returns some knowledge, its format might be not what the appliance is anticipating. I wanted some runtime validation mechanism to validate the schema of the response. I’m fortunate as soon as extra as a result of as an alternative of implementing a runtime validation mechanism from scratch, I can use one other open supply library: [io-ts](https://github.com/gcanti/io-ts).

The answer #

TL;DR This part explains the implementation particulars of the answer. Be happy to skip this half and soar into “The outcome” part in case you are solely within the last client API.

The io-ts module permits us to declare a schema that can be utilized to carry out validation at runtime. We are able to additionally use io-ts to generate varieties from a given schema. Each of those options are showcased within the following code snippet:

import * as io from "io-ts";

export const ActivityValidator = io.sort({
    startTime: io.string,
    title: io.string,
    minuteCount: io.quantity

export const ActivityArrayValidator = io.array(ActivityValidator);

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

We are able to use the decode technique to validate that some knowledge adheres to a schema. The validation outcome returned by decode is an Both occasion, which implies that we’ll both get a validation error (left) or some legitimate knowledge (proper).

My first step was to wrap the fetch API, so it makes use of each fp-ts and io-ts to make sure that the response is and Both that represents an error (left) or some legitimate knowledge (proper). By doing this, the promise returned byfetch is rarely rejected. As an alternative, it’s all the time resolved as an Both occasion:

import { Both, Left, Proper } from "fp-ts/lib/Both";
import { Sort, Errors} from "io-ts";
import { reporter } from "io-ts-reporters";

export async operate fetchJson<T, O, I>(
    url: string,
    validator: Sort<T, O, I>,
    init?: RequestInit
): Promise<Both<Error, T>> {
    attempt {
        const response = await fetch(url, init);
        const json: I = await response.json();
        const outcome = validator.decode(json);
        return outcome.fold<Both<Error, T>>(
            (errors: Errors) => {
                const messages = reporter(outcome);
                return new Left<Error, T>(new Error(messages.be a part of("n")));
            (worth: T) => {
                return new Proper<Error, T>(worth);
    } catch (err) {
        return Promise.resolve(new Left<Error, T>(err));

Then I created a React part named Distant that takes an Both occasion as one among its properties along with some rendering features. The info will be both null | Error or some worth of sort T.

The loading operate is invoked when the info is null, the error is invoked when the info is an Error and the success operate is invoked when knowledge is a worth of sort T:

import React from "react";
import { Both } from "fp-ts/lib/both";

interface RemoteProps<T>  null, T>;
  loading: () => JSX.Component,
  error: (error: Error) => JSX.Component,
  success: (knowledge: T) => JSX.Component

interface RemoteState {}

export class Distant<T> extends React.Part<RemoteProps<T>, RemoteState> {

  public render() {
    return (
          l => {
            if (l === null) {
              return this.props.loading();
            } else {
              return this.props.error(l);
          r => {
            return this.props.success(r);


export default Distant;

The above part is used to render an Both occasion, however it doesn’t carry out any knowledge fetching operations. As an alternative, I carried out a second part named Fetchable which takes an url and a validator along with some optionally available RequestInit configuration and a few rendering features. The part makes use of the fetch wrapper and the validator to fetch some knowledge and validate it. It then passes the ensuing Both occasion to the Distant part:

import { Sort } from "io-ts";
import React from "react";
import { Both, Left } from "fp-ts/lib/Both";
import { fetchJson } from "./consumer";
import { Distant } from "./distant";

interface FetchableProps<T, O, I> {
    url: string;
    init?: RequestInit,
    validator: Sort<T, O, I>
    loading: () => JSX.Component,
    error: (error: Error) => JSX.Component,
    success: (knowledge: T) => JSX.Component

interface FetchableState<T>  null, T>;

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

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

    public componentDidMount() {
        (async () => {
            const outcome = await fetchJson(
                knowledge: outcome

    public render() {
        return (


The outcome #

I’ve launched all of the previous supply code as a module named react-fetchable. You may set up the module utilizing the next command:

npm set up io-ts fp-ts react-fetchable

You may then import the Fetchable part as follows:

import { Fetchable } from "react-fetchable";

At this level I can implement the web page that I described on the beguinning:

import React from "react";
import Container from "../../elements/container/container";
import Part from "../../elements/part/part";
import Desk from "../../elements/desk/desk";
import { IActivityArray, ActivityArrayValidator } from "../../lib/area/varieties";
import { Fetchable } from "react-fetchable";

interface ScheduleProps {}

interface ScheduleState {}

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

export default Schedule;

I can move the URL /knowledge/schedule.json to the Fetchable part along with a validator ActivityArrayValidator. The part will then:

  1. Render Loading...
  2. Fetch the info
  3. Render a desk if the info is legitimate
  4. Render an error is the info can’t be loaded doesn’t adhere to the validator

I’m proud of this resolution as a result of it’s type-safe, declarative and it solely takes just a few seconds to get it up and working. I hope you will have discovered this submit fascinating and that you simply attempt react-fetchable.

Additionally, in case you are curious about Practical Programming or TypeScript, please try my upcoming e book Palms-On Practical Programming with TypeScript.









Please enter your comment!
Please enter your name here

Most Popular

Recent Comments