Another liblary for restful resources management for React app.


Keywords
react, react-native, rest, restful, typescript, request, front-end, api, fetch
License
MIT
Install
npm install react-restful@2.0.6

Documentation

react-restful

Another liblary for restful resources management for React app.

NPM

Build Status Coverage Status

readme.pngAre you familiar with Swagger?

Why this is helpful?

  • Centralized all API resources in one place, for management and maintenance.
  • Monitor data changes and automatically update view.
  • Based on typescript, no redux.
  • Easy to learn, simple integration without compromising performance.
  • Small size bundle.

Demo

See how react-restful work with simple CRUD app (thanks strapi and heroku for backend system).

Edit o4n7n0o7x6

Quick start

Install and setup

Install package from npm:

npm install react-restful

Create restful folder under ./src, follow code:

/**
 * file: /src/restful/environment.ts
 */
import { setupEnvironment } from 'react-restful';

const restfulEnv = setupEnvironment({
    /**
     * API's URI
     */
    entry: 'https://api.domain.com',
    /**
     *  Called before fetch the request
     *  Example: Init the bearer token into the header for authentication
     */
    beforeFetch: (url: string, requestInit: RequestInit) => {
        const token = getToken();
        if (token && requestInit.headers instanceof Headers) {
            requestInit.headers.append('Authorization', `Bearer ${token}`);
        }
        return requestInit;
    },
    /**
     * Called after the fetch
     */
    afterFetch: async (requestInfo) => {
        const { response } = requestInfo
        
        if (response.ok) {
            return;
        }

        const error = await response.json();
        console.error({
            error
        });
    }
});

const { request } = setupEnvironment;

// Using request to send PUT, POST or DELETE.
export const request = request;

And then, create pet.ts file to define everything related to Pet API

/**
 * file: /src/restful/resource/pet.ts
 */

import { Record, Resource, ResourceType } from 'react-restful';

/**
 * Data structure of Pet, 
 * extends Record needed
 */
export interface Pet extends Record {
  id?: number;
  name: string;
  desciption?: string;
}

/** 
 * All pet data has the same structure, 
 * we need to specify this structure (like a database table)
 * */
export const petResourceType = new ResourceType<Pet>('Pet');

/**
 * APIs with basic infomation
 */
export const petResources = {
    find: new Resource<Pet[]>({
        resourceType: petResourceType,
        url: '/pets'
    }),
    findById: new Resource<Pet[]>({
        resourceType: petResourceType,
        url: '/pets/:id'
    }),
    create: new Resource<Pet>({
        resourceType: petResourceType,
        method: 'POST',
        url: '/pets'
    }),
    update: new Resource<Pet>({
        resourceType: petResourceType,
        method: 'PUT',
        url: '/pets/:id'
    }),
    delete: new Resource<Pet>({
        resourceType: petResourceType,
        method: 'DELETE',
        url: '/pets/:id'
    })
};

Export via index.ts

/**
 * file: /src/restful/index.ts
 */

export * from './resful-environment';
export * from './resources/pet';

Send a GET request

import { RestfulRender, RestfulComponentRenderProps } from 'react-restful';

import { petResources } from '../restful';

export class PetContainer extends React.Component {
    render() {
        return (
            <RestfulRender
                resource={petResources.find}
                render={this.renderList}
            />
        );
    }

    renderList = (renderProps: RestfulComponentRenderProps<Pet[]>) => {
        const { data, error, fetching } = renderProps;

        if(error) {
            return 'error!';
        }

        if (!data) {
            return 'loading...';
        }

        return <PetList pets={data} showLoading={fetching} />;
    };
}

In case you want send GET request with query string e.g: /pets?name='micky'

    <RestfulRender
        resource={petResources.find}
        parameters={{
            type: 'query',
            parameter: 'name',
            value: 'micky'
        }}
        render={this.renderList}
    />

Or more than one /pets?name='micky'&type='mouse'

const parameters: RequestParams = [{
    type: 'query',
    parameter: 'name',
    value: 'micky'
}, {
    type: 'query',
    parameter: 'type',
    value: 'mouse'  
}];

With findById resource in above, use param type path to replace /:id with object's id:

// Form "/pets/:id"
// To "/pets/1"

const parameter: RequestParameter = {
    type: 'path',
    parameter: 'id',
    value: 1
};

Send POST, PUT or DELETE:

import { RequestParams } from 'react-restful';
import { request, petResources } from '../restful';

const createNewPet = async () => {
    const requestParams: RequestParams = {
        type: 'body',
        value: {
            name: 'tom',
            type: 'cat'
        }
    };

    const newPet = await request(petResources.create, requestParams);
    return newPet;
}

const updatePet = async () => {
    import { RequestParams } from 'react-restful';

    const requestParams: RequestParams = [{ 
            type: 'path', 
            parameter: 'id', 
            value: 1 
        },
        { 
            type: 'body', 
            value: { name: 'change pet name to abc'}
        }];

    const updatedPet = await request(petResources.update, requestParams);
    return updatedPet;
};

const deletePet = async () => {
    const { pet } = this.props;

    await request(petResources.delete, {
        type: 'path',
        parameter: 'id',
        value: 1
    });
};

Sync restful data with view

We need to create Higher Order Component to render Child Component every time data(passed down from RestfulRender ) changes, including create, update or delete event.

import { withRestfulData } from "react-restful";

import { Pet, petResourceType } from "../restful";

interface PetListsProps {
    showLoading: boolean;
    pets: Array<Pet>;
}

// Component to display list of pet
function PetListComponent(props: PetListsProps) {
  return // ...
}

export const PetList = withRestfulData<Pet, PetListsProps>({
    resourceType: petResourceType,
    registerToTracking: (
        ownProps: PetListsProps,
        trackedPets: Array<Pet>,
        event: SubscribeEvent<Pet>
    ) => {
        if (trackedPets && trackedPets.length) {
            return trackedPets;
        }
        
        return ownProps.pets;
    },
    mapToProps: (
        pets: Array<Pet>,
        ownProps: PetListsProps
    ) => {
        return {
            pets: pets
        };
    }
})(PetListComponent);

Checkout the demo above to more details.

Documents

On progress...