Common MobX abstract base Class & Decorator utilities for RESTful API


Keywords
restful, api, mobx, sdk, model, store, state, typescript
License
LGPL-3.0
Install
npm install mobx-restful@2.1.0-rc.1

Documentation

MobX RESTful

Common MobX abstract base Class & Decorator utilities for RESTful API.

Just define your Data models & Client HTTP methods, then leave rest of things to MobX!

MobX compatibility NPM Dependency CI & CD

NPM

Versions

SemVer status ES decorator MobX
>=0.7.0 ✅developing stage-3 >=6.11
<0.7.0 ❌deprecated stage-2 >=4 <6.11

Usage

package.json

{
    "dependencies": {
        "koajax": "^3.0.0",
        "mobx": "^6.13.5",
        "mobx-restful": "^2.0.0"
    }
}

tsconfig.json

{
    "compilerOptions": {
        "target": "ES6",
        "moduleResolution": "Node",
        "useDefineForClassFields": true,
        "experimentalDecorators": false,
        "jsx": "react-jsx"
    }
}

Simple List

model/client.ts

import { HTTPClient } from 'koajax';

export const client = new HTTPClient({
    baseURI: 'https://api.github.com/',
    responseType: 'json'
});

model/Repository.ts

import { buildURLData } from 'web-utility';
import { Filter, ListModel } from 'mobx-restful';
import { components } from '@octokit/openapi-types';

import { client } from './client';

export type Organization = components['schemas']['organization-full'];
export type Repository = components['schemas']['minimal-repository'];

export class RepositoryModel<
    D extends Repository = Repository,
    F extends Filter<D> = Filter<D>
> extends ListModel<D, F> {
    client = client;
    baseURI = 'orgs/idea2app/repos';

    async loadPage(page: number, per_page: number) {
        const { body } = await this.client.get<D[]>(
            `${this.baseURI}?${buildURLData({ page, per_page })}`
        );
        const [_, organization] = this.baseURI.split('/');
        const {
            body: { public_repos }
        } = await this.client.get<Organization>(`orgs/${organization}`);

        return { pageData: body, totalCount: public_repos };
    }
}

export default new RepositoryModel();

page/Repository.tsx

Use WebCell as an Example

import { component, observer } from 'web-cell';

import repositoryStore from '../model/Repository';

@component({ tagName: 'repository-page' })
@observer
export class RepositoryPage extends HTMLElement {
    connectedCallback() {
        repositoryStore.getList();
    }

    disconnectedCallback() {
        repositoryStore.clear();
    }

    render() {
        const { currentPage } = repositoryStore;

        return (
            <ul>
                {currentPage.map(({ full_name, html_url }) => (
                    <li key={full_name}>
                        <a target="_blank" href={html_url}>
                            {full_name}
                        </a>
                    </li>
                ))}
            </ul>
        );
    }
}

Preload List

model/PreloadRepository.ts

import { buildURLData } from 'web-utility';
import { Buffer } from 'mobx-restful';

import { client } from './client';
import { Repository, RepositoryModel } from './Repository';

export class PreloadRepositoryModel extends Buffer<Repository>(
    RepositoryModel
) {
    client = client;
    baseURI = 'orgs/idea2app/repos';

    loadPage = RepositoryModel.prototype.loadPage;
}

export default new PreloadRepositoryModel();

Multiple Source List

model/MultipleRepository.ts

import { buildURLData, mergeStream } from 'web-utility';
import { Stream } from 'mobx-restful';
import { components } from '@octokit/openapi-types';

import { client } from './client';
import { Repository, RepositoryModel } from './Repository';

export type User = components['schemas']['public-user'];

export class MultipleRepository extends Stream<Repository>(RepositoryModel) {
    declare baseURI: string;
    client = client;

    async *getOrgRepos() {
        const {
            body: { public_repos }
        } = await this.client.get<Organization>('orgs/idea2app');

        this.totalCount = public_repos;

        for (let i = 1; ; i++) {
            const { body } = await this.client.get<Repository[]>(
                'orgs/idea2app/repos?page=' + i
            );
            if (!body[0]) break;

            yield* body;
        }
    }

    async *getUserRepos() {
        const {
            body: { public_repos }
        } = await this.client.get<User>('users/TechQuery');

        this.totalCount = public_repos;

        for (let i = 1; ; i++) {
            const { body } = await this.client.get<Repository[]>(
                'users/TechQuery/repos?page=' + i
            );
            if (!body[0]) break;

            yield* body;
        }
    }

    openStream() {
        return mergeStream(
            this.getOrgRepos.bind(this),
            this.getUserRepos.bind(this)
        );
    }
}

export default new MultipleRepository();

File Downloader

This module has been moved to MobX-downloader since MobX-RESTful v2.

Wrapper

  1. Strapi v4
  2. GitHub
  3. Lark/FeiShu

Component

  1. Table, List & Form suite

Scaffold

  1. Client-side Rendering (React): https://github.com/idea2app/Next-Bootstrap-ts
  2. Server-side Rendering (React): https://github.com/idea2app/React-MobX-Bootstrap-ts
  3. Cross-end App (React): https://github.com/idea2app/Taro-Vant-MobX-ts

Limitation