MobX RESTful
    Preparing search index...

    MobX RESTful

    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

    SemVer status ES decorator MobX
    >=0.7.0 ✅developing stage-3 >=6.11
    <0.7.0 ❌deprecated stage-2 >=4 <6.11
    npm install koajax mobx mobx-restful
    
    {
    "compilerOptions": {
    "target": "ES6",
    "moduleResolution": "Node",
    "useDefineForClassFields": true,
    "experimentalDecorators": false,
    "jsx": "react-jsx"
    }
    }
    import { HTTPClient } from 'koajax';

    export const client = new HTTPClient({
    baseURI: 'https://api.github.com/',
    responseType: 'json'
    });
    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();

    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>
    );
    }
    }
    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();
    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();

    @persist() & restore() functions give us a declarative way to save & restore data to/from IndexedBD, such as these following examples:

    import { observable } from 'mobx';
    import { BaseModel, persist, restore, toggle } from 'mobx-restful';
    import { Day } from 'web-utility';

    import { client } from './client';
    import { User } from './User';

    export class SessionModel extends BaseModel {
    baseURI = 'session';
    client = client;

    @persist({ expireIn: 15 * Day })
    @observable
    user?: User;

    restored = restore(this, 'Session');

    @toggle('uploading')
    async signIn(username: string, password: string) {
    const { body } = await this.client.post<User>('session', {
    username,
    password
    });
    return (this.user = body);
    }
    }

    export default new Session();

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

    import { ListModel, persistList } from 'mobx-restful';

    import { Gift } from '@my-scope/service-type';

    import { client } from './client';

    @persistList({
    storeKey: ({ partyId }) => `PartyGift-${partyId}`,
    expireIn: 2 * Day
    })
    export class PartyGiftModel extends ListModel<Gift> {
    baseURI = 'party';
    client = client;

    constructor(public partyId: number) {
    super();
    this.baseURI += `/${partyId}/gift`;
    }

    protected async loadPage(pageIndex: number, pageSize: number) {
    const { body } = await this.client.get<{
    count: number;
    list: Gift[];
    }>(`${this.baseURI}?${buildURLData({ pageIndex, pageSize })}`);

    return { pageData: body.list, totalCount: body.count };
    }
    }

    This example page uses Cell Router to pass in partyId route parameter:

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

    import { PartyGiftModel } from '../../model/Party/Gift';

    @component({ tagName: 'party-gift-page' })
    @observer
    export class PartyGiftPage extends HTMLElement {
    @attribute
    @observable
    accessor partyId = 0;

    @observable
    accessor store: PartyGiftModel | undefined;

    async connectedCallback() {
    this.store = new PartyGiftModel(this.partyId);

    await this.store.restored;
    // this calling will do nothing after the first loading
    // in list cache period
    await this.store.getAll();
    }

    render() {
    const { allItem } = this.store || {};

    return (
    <>
    <h1>Gift wall</h1>
    <ol>
    {allItem.map(({ name, price }) => (
    <li key={name}>
    {name} - {price}
    </li>
    ))}
    </ol>
    </>
    );
    }
    }
    1. Strapi v4/5
    2. GitHub
    3. Lark/FeiShu
    1. Data Migrator
    2. File Downloader
    1. Table, List & Form suite
    1. Client-side Rendering (React): https://github.com/idea2app/React-MobX-Bootstrap-ts
    2. Client-side Rendering (Vue): https://github.com/idea2app/Vue-MobX-Prime-ts
    3. Server-side Rendering (React): https://github.com/idea2app/Next-Bootstrap-ts
    4. Cross-end App (React): https://github.com/idea2app/Taro-Vant-MobX-ts