npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

http-api-orm

v1.3.0

Published

A api orm implemented by ts 。一个ts的api-orm。

Downloads

2

Readme

安装

npm install http-api-orm

使用 http-api-orm 创建项目

npm init http-api-orm

简要说明

一个ts的对于api的orm。
参考数据库orm,将一个接口视为一个模型,底层使用axios发起请求。虽然通常一组接口对应一张表。但由于接口的性质,也只能如此了。

主要提供一个 ApiModel 类,用于配置请求配置,管理响应数据,配置拦截器中间件(同axios,但会按顺序执行,可拆分处理逻辑)。

注意问题

import ApiModel, {Params} from "http-api-orm";
import {reactive} from "vue";

// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}

class MyApiModel extends ApiModel {
    // Params 也提供 proxyData
    params = new Params(() => ({
        id: '',
        detail: true
    })).proxyData()
    data = {
        hasChildren: true,
        id: "",
        level: 0,
        name: "",
        note: "",
        org: 0,
        pid: "",
        state: 0,
    }

    test() {
        //在内部也可用proxy
        const proxy = this.proxyData()
        proxy.pid = '4555'
    }
}

//vue3 中使用  reactive 注意 as 类型,以避免 传入 子组件时,类型异常
const api1 = reactive(new MyApiModel()) as MyApiModel;
//代理访问类型
//推荐如下两种类型设置。在 template 中 也能准确提供类型识别。
const apiProxy = reactive(new MyApiModel().proxyData()) as MyApiModel & MyApiModel['data'];
// const temp  = new MyApiModel().proxyData()
// const api = reactive(temp) as typeof temp

//如下方式标记类型,在template中可能会类型提示不够准确
// const apiProxy = reactive(new MyApiModel().proxyData()) as ReturnType<MyApiModel['proxyData']>;

基础使用

import ApiModel, {Params} from "http-api-orm";
import type {ApiResponseMid} from "http-api-orm/types/lib/ApiModel";
import {reactive} from "vue";

// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}

class MyApiModel extends ApiModel {
    params = new Params(() => ({
        id: '',
        detail: true
    })).proxyData()
    data = {
        hasChildren: true,
        id: "",
        level: 0,
        name: "",
        note: "",
        org: 0,
        pid: "",
        state: 0,
    }
}

const api1 = new MyApiModel();

//设置参数
apiM.params.id = '123'

apiM.getResData().then(r => {
//    此时 r为 axios 响应 AxiosResponse
//    此时直接使用 apiM.resData
    console.log(apiM.data.name)
})

//模型代理数据
const apiProxyData = new MyApiModel().proxyData();
//相当于  console.log(apiProxyData.data.name)
console.log(apiProxyData.name)

ApiModel 配置 查看 类型文件,有详细注释

基础使用

import ApiModel, {Params} from "http-api-orm";

export default class MyApiModel extends ApiModel {
    url = '/xx/xx'
    //method 默认 get
    // method = 'get'
    //请求get参数,
    params = new Params(() => ({
        id: '',
        detail: true
    }))
    //post参数 且  代理访问 数据
    postData = new Params(() => ({
        postId: '',
        postDetail: true
    })).proxyData()
}

const apiM = new MyApiModel();
//vue3 使用,可作为响应式对象
// const apiM = reactive(new MyApiModel()) as MyApiModel;

//设置参数
apiM.params.data.id = '123'

//设置代理后,可跳过直接访问data中的数据
apiM.postData.postId = 'post'

apiM.getResData().then(r => {
//    此时 r为 axios 响应 AxiosResponse
})

使用模型数据

import ApiModel, {Params} from "http-api-orm";
import type {ApiResponseMid} from "http-api-orm/types/lib/ApiModel";

// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}

export default class MyApiModel extends ApiModel {
    url = '/xx/xx'
    params = new Params(() => ({
        id: '',
        detail: true
    })).proxyData()
    data = {
        hasChildren: true,
        id: "",
        level: 0,
        name: "",
        note: "",
        org: 0,
        pid: "",
        state: 0,
    }
    //如何接受 响应数据,由于各种项目 接口格式不一致,自行配置,假定接口正确
    resMid: ApiResponseMid[] = [
        function (r, m) {
            if (m) {
                m.data = r.data.content
            }
            return r
        }
    ]
}

const apiM = new MyApiModel();

//设置参数
apiM.params.id = '123'

apiM.getResData().then(r => {
//    此时 r为 axios 响应 AxiosResponse
//    此时直接使用 apiM.resData
    console.log(apiM.data.name)
})

//模型代理数据
const apiProxyData = new MyApiModel().proxyData();
//相当于  console.log(apiProxyData.data.name)
console.log(apiProxyData.name)



使用loading

//方案一  指令方式
import ApiModel, {Params} from "http-api-orm";
import {reactive} from "vue";

//以 vue 3为例
const tsx =
    <template>
        {/*api.loading 会根据请求自动切换是否加载中 */}
        <el-table v-loading={api1.loading}>
            <!--  any-->
        </el-table>
    </template>


class MyApiModel extends ApiModel {
    url = '/xx/xx'
    params = new Params(() => ({
        id: '',
        detail: true
    })).proxyData()
}

//设置响应式
const api1 = reactive(new MyApiModel()) as MyApiModel;

//设置参数
api1.params.id = '123'

api1.getResData().then(r => {
//    此时 r为 axios 响应 AxiosResponse
})


//方案2  调用方法式
import ApiModel, {Loading, Params} from "http-api-orm";
import {reactive} from "vue";
import {ElLoading} from "element-plus";
import {LoadingInstance} from "element-plus/es/components/loading/src/loading";
import {LoadingOptions} from "element-plus/es/components/loading/src/types";

class MyApiLoadingModel extends ApiModel {
    url = '/xx/xx'
    params = new Params(() => ({
        id: '',
        detail: true
    })).proxyData()
    loadingMan = new Loading<LoadingInstance, LoadingOptions>(function (o) {
        //如何开始loading 并返回 实例,如果存在
        return ElLoading.service(o)
    }, function (inst) {
        //如何关闭loading inst 为 可能存在的 loading实例
        inst?.close();
    }, {
        //配置请求发起后延迟多开始loading
        delayStartMs: 1000,
        //配置请求结束延迟多关闭loading
        delayCloseMs: 1000
    })
}

const api2 = reactive(new MyApiLoadingModel()).proxyData() as ReturnType<MyApiLoadingModel['proxyData']>;

//设置参数
api2.params.id = '123'

api2.getResData().then(r => {
//    此时 r为 axios 响应 AxiosResponse
})

取消请求

import ApiModel, {CancelMan, Params} from "http-api-orm";
import {reactive} from "vue";

export default class MyApiLoadingModel extends ApiModel {
    url = '/xx/xx'
    params = new Params(() => ({
        id: '',
        detail: true
    }))
    //未配置的无法中断请求
    //取消请求控制器,默认 标记 default   可传入 symbol 作为唯一标记
    cancelMan = new CancelMan('default')
}

const api = reactive(new MyApiLoadingModel()) as MyApiLoadingModel;

//设置参数
api.params.data.id = '123'

api.getResData().then(r => {
})

//取消请求  标记固定为 创建时传入的,,使用symbol 创建时,仅使用实例可以取消
api.cancelMan.cancel()
// 取消给定标记的 所有请求,不传默认 default
CancelMan.cancel()

axios 默认配置

import ApiModel from "http-api-orm";
import type {AxiosRequestConfig} from "axios";

export default class MyApiLoadingModel extends ApiModel {
    //要从此类开始,启用一个新的默认配置
    protected static _defaultConfig: AxiosRequestConfig = {}

    constructor() {
        super();
        //通过defaultConfig 设置的axios 请求信息,会在所有模型中使用,,存储在静态变量中
        this.defaultConfig.headers = this.defaultConfig.headers ?? {}
        this.defaultConfig.headers.token = 'xxx'
        //清空默认配置
        this.clearDefaultConfig()
    }
}

拦截器,中间件说明

import ApiModel from "http-api-orm";
import axios from "axios";
import type {ApiFinallyMid, ApiRequestMid, ApiResponseMid} from "http-api-orm/types/lib/ApiModel";
// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}
export default class MyApiModel extends ApiModel {
    //请求前处理
    reqMid: (ApiRequestMid)[] = [
        function (c, m) {
            console.log('reqMid1', m?.loading)
            //若此处 抛出异常 或 reject 将跳过 后续 reqMid 和 resMid 进入 finallyMid
            // throw new Error()
            // return Promise.reject('')
            // m?.cancelMan?.cancel()
            return c
        },
        function (c, m) {
            console.log('reqMid2')
            return c
        },
    ]
    //响应处理
    resMid: ApiResponseMid[] = [
        function (r, m) {
            console.log('resMid1', m?.loading)
            //若此处 抛出异常 或 reject 将跳过 后续 resMid  进入 finallyMid
            // throw new Error()
            // return Promise.reject('')
            return r
        },
        function (r, m) {
            console.log('resMid2', m?.loading)
            if (r.status === 200 && r.data.status === 1) {
                if (m) {
                    //模型数据如何获取
                    m.data = r.data.content
                }
            } else {
                //格式不正确,返回异常
                //可在此处展示发起提示
                return Promise.reject(r.data.msg)
            }
            return r
        },
    ]
    //结束处理, 同 promise.finally
    finallyMid: ApiFinallyMid[] = [
        function (e, m) {
            console.log('f', 'isCancel', m?.loading)
            //若此处 抛出异常 或 reject 仍然会执行后续方法
            // throw new Error()
            // return Promise.reject('')
            if (axios.isCancel(e)) {
                console.log('isCancel', e)
            }
        },
        function (e, m) {
            console.log('f', 'isAxiosError')
            if (axios.isAxiosError(e)) {
                console.log('isAxiosError', e)
            }
            return e
        },
        function (e, m) {
            console.log('f', 'isError')
            if (m?.request.isError(e)) {
                console.log('error', e)
            }
        },
        function (e, m) {
            console.log('f', 'isAxiosResponse')
            if (m?.request.isAxiosResponse(e)) {
                console.log('isAxiosResponse', e)
            }
        },
    ]
}

中等复杂度请求与简单请求

import ApiModel, {Params} from "http-api-orm";
// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}
export default class MyApiModel extends ApiModel {
    //中等复杂度请求
    list() {
        
        //根据当前实例创建一个新的实例,但使用传入的参数替换 原有的参数,并获得类型支持
        //会代理访问数据
        return this.createNew({
                url: '/xxx/list',
                params: new Params(() => ({page: 1, pageSize: 20, stauts: 0})),
                data: {total: 0, list: [] as any[]}
            }
        )
    }

    //简单请求,修改局部数据
    changeState(s: boolean) {
        //此时传入的配置优先级最高  此时传入 url 会使用 url
        //而 getResData 时,模型配置的参数优先级最高   此时  此时传入 url 仍会使用 this.url
        return this.request.post('/xx/xx', {id: this.data.id, state: s}, {})
    }
}

完整模型示例

import ApiModel, {CancelMan, Loading, Params} from "http-api-orm";
import axios, {Method} from "axios";
import type {AxiosRequestConfig, AxiosInstance} from "axios";
import type {ApiFinallyMid, ApiRequestMid, ApiResponseMid} from "http-api-orm/types/lib/ApiModel";
import {ElLoading} from "element-plus";
import type {LoadingInstance} from "element-plus/es/components/loading/src/loading";
import type {LoadingOptions} from "element-plus/es/components/loading/src/types";
// 假定接口返回数据格式为
const apiInfo = {
    //1 成功
    status: 1,
    msg: '成功',
    //content 为 数据内容 任意格式
    content: {}
}
//参数复杂,模型数据复杂,操作逻辑复杂的情况下,应当新建一个模型。
//较为简单的请求,可以使用 request 直接请求。
export default class UserModel extends ApiModel {
    //响应数据
    // resData: any
    //定义一个 axios实例,可选的
    http: AxiosInstance = axios.create()
    //接口地址 可选的,默认 ''
    url = '/user/detail';
    //默认 get
    method: Method = 'post';
    //默认配置,静态,,此处定义,则 从让后代使用一个新的默认配置   在实例中使用 defaultConfig 获取配置
    protected static _defaultConfig: AxiosRequestConfig = {}

    // get 请求参数 可选
    params = new Params(() => ({id: ''}))
    // post 请求参数 可选
    postData = new Params(() => ({}))
    //未配置的无法中断请求
    //取消请求控制器,默认 标记 default   可传入 symbol 作为唯一标记
    cancelMan = new CancelMan('default')

    //接口状态 是否请求中
    // loading: boolean = false

    //请求管理配置
    loadingMan = new Loading<LoadingInstance, LoadingOptions>(function (o) {
        //如何开始loading 并返回 实例,如果存在
        return ElLoading.service(o)
    }, function (inst) {
        //如何关闭loading inst 为 可能存在的 loading实例
        inst?.close();
    }, {
        //配置请求发起后延迟多开始loading
        delayStartMs: 1000,
        //配置请求结束延迟多关闭loading
        delayCloseMs: 1000
    })


    //请求前处理
    reqMid: (ApiRequestMid)[] = [
        function (c, m) {
            console.log('reqMid1', m?.loading)
            //若此处 抛出异常 或 reject 将跳过 后续 reqMid 和 resMid 进入 finallyMid
            // throw new Error()
            // return Promise.reject('')
            // m?.cancelMan?.cancel()
            return c
        },
        function (c, m) {
            console.log('reqMid2')
            return c
        },
    ]
    //响应处理
    resMid: ApiResponseMid[] = [
        function (response, m) {
            console.log('resMid1', m?.loading)
            //若此处 抛出异常 或 reject 将跳过 后续 resMid  进入 finallyMid
            // throw new Error()
            // return Promise.reject('')

            if (response.config.responseType === 'blob') {
                //假设本项目下载文件处理为此.下载文件不应担作为 getResData
                let aTag = document.createElement("a");
                aTag.download = decodeURIComponent(
                    response.headers["content-disposition"]
                ).substring(20);
                aTag.href = URL.createObjectURL(response.data);
                aTag.click();
                URL.revokeObjectURL(response.data);
            }

            return response
        },
        function (r, m) {
            console.log('resMid2', m?.loading)
            if (r.status === 200 && r.data.status === 1) {
                if (m) {
                    //模型数据如何获取
                    m.data = r.data.content
                }
            } else {
                //格式不正确,返回异常
                //可在此处展示发起提示
                return Promise.reject(r)
            }
            return r
        },
    ]
    //结束处理, 同 promise.finally
    finallyMid: ApiFinallyMid[] = [
        function (e, m) {
            console.log('f', 'isCancel', m?.loading)
            //若此处 抛出异常 或 reject 仍然会执行后续方法
            // throw new Error()
            // return Promise.reject('')
            if (axios.isCancel(e)) {
                console.log('isCancel', e)
            }
        },
        function (e, m) {
            console.log('f', 'isAxiosError')
            if (axios.isAxiosError(e)) {
                console.log('isAxiosError', e)
            }
            return e
        },
        function (e, m) {
            console.log('f', 'isError')
            if (m?.request.isError(e)) {
                console.log('error', e)
            }
        },
        function (e, m) {
            console.log('f', 'isAxiosResponse')
            if (m?.request.isAxiosResponse(e)) {
                console.log('isAxiosResponse', e)
            }
        },
    ]

    //模型数据
    data = {
        id: "",
        name: "",
        //0 禁用,1 正常
        state: 0 as 0 | 1,
        createTime: undefined,
        //会员等级,0-4
        vipLevel: 0 as 0 | 1 | 2 | 3 | 4,
        note: "",
        org: 0,
    }

    //会员名称
    get vipName() {
        return {
            0: '无',
            1: '初级会员',
            2: '中级会员',
            3: '超级会员',
            4: '黑卡会员',
        }[this.data.vipLevel]
    }

    //获取用户列表,这里加入,列表和详情数据一致,列表请求参数也简单,可以使用一个模型
    getList() {
        // 相当于  return  new UserModel()  但参数使用传入的参数,,快速创建了一个接口模型
        return this.createNew({
            //这里选项,同 ApiModel 配置项
            url: '/user/list',
            params: new Params(() => ({a: 1})).proxyData()
        })
    }

    //保存信息
    save() {
        return this.request.post('/user/save', this.data)
    }


    //修改局部数据
    saveState(s: boolean) {
        return this.request.post('/xx/xx', {id: this.data.id, state: s}, {})
    }

    //下载
    down(c: AxiosRequestConfig) {
        return this.request.request({
            ...c,
            responseType: 'blob',
        })
    }

//    更多其他模型操作
}