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

koa-lambda-middleware

v1.0.6

Published

Downloads

75

Readme

koa-lambda-middleware

一接口一函数,传一参,反一世界。

koa-lambda中间件提供lambda方式进行接口开发,保持简单,使用函数式方式,约定统一仅使用post请求,去TMD的RESTful,降低心智负担才是生产效率。

虽然koa原生的中间件为同为函数式,但是其实函数的传参可以进一步优化,并抽象到前端接口调用的传参,并做统一。而且return返回值也没有充分利用上,因此koa-lambda中间件弥补这些不足带来了这些便利。

开发要高效,不仅需要代码和配置量少,代码逻辑清晰,易于维护,而且框架/库提供的规范需要符合直觉,约定要优于配置。

安装

npm i -s koa-lambda-middleware

初始化koaLambda中间件

koaLambda中间件依赖 body parsers中间件,这里以koa-body为例(其实只要解析为ctx.request.body就行)

const Koa = require('koa');
const { koaBody } = require('koa-body');
const koaLambda = require('koa-lambda-middleware');
const app = new Koa();


app.use(koaBody())
    .use(koaLambda({}, app)); // 初始化koaLambda中间件

app.listen(3333);

koaLambda为初始化方法,传参为(<配置>,<当前koa app实例>) 返回中间件。

配置说明(默认值)为

{ 
  handlerAopDefault: "",        //函数逻辑在next前还是在next后,为空无next   值有:'after' | 'before' | ''
  root: "",                     //http请求访问路径头,如果配置baz,接口访问都统一 http://localhost/baz/**下
  dirname: __dirname + "/src",  //源码目录,递归获取所有src下的模块js文件
  filter: /(.*)\.js$/,          //过滤器  可以是 正则或函数
}



// koaLambda({}, app) 相当于默认配置:
koaLambda({
  handlerAopDefault: "",
  root: "",
  dirname: __dirname + "/src",
  filter: /(.*)\.js$/, 
}, app)

路由

在src目录下创建一个js模块文件hello.js。定义一个hello方法,这里后面我们统一称为Lambda函数,函数的路径为/hello/hello

module.exports = {
  hello(){
    return "hello world!"
  }
};

对应访问地址为:http://localhost/hello/hello 路由地址:文件名/函数路径

不过没有特定配置(默认)情况下,需要httpPOST才能正常请求和响应,这是约束或者说更是约定

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 11
Date: Mon, 28 Feb 2022 03:20:46 GMT
Connection: close

hello world

允许出现这种嵌套方式定义Lambda函数。但是注意不要在目录里面出现同路径文件名,请保持路由唯一

module.exports = {
  a:{
    b:{
      c(){
         return {path:'a.b.c'}
      }
    }
  }
};

接口地址为:http://localhost/path/a/b/c

传参

传参上面的约定,前端以application/json(取决于bodyparsers中间件)传args数组为参数数组

POST http://localhost:3333/a/foo HTTP/1.1
content-type: application/json

{
    "args":[
        2,
        3
    ]
}

创建一个a.js文件为例,返回传参之和,这里Lambda函数的参数a, b分别对应接口请求传入的args数组

module.exports = {
  foo(a, b){ // 这里a, b分别为2, 3
    return {sum: a + b}
  }
};

响应返回(即为函数return值)如下:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 9
Date: Mon, 28 Feb 2022 06:41:51 GMT
Connection: close

{
  "sum": 5
}

Hook

在Lambda函数中使用了类似React hook的方式,通过使用 useContext 获取ctx, 使用 useNext 获取next。可以说是一次曲线救国,解决纯函数没有 ctx 和 next。

const { useContext, useNext } = require('koa-lambda-middleware');

module.exports = {
  async foo(){
    let ctx = useContext();
    let next = useNext();
    
    ctx.body = 'ok!';
    
    await next();
  }
};

这里需要注意的是如果next后置同时需要返回,方法的return就不应该使用了,应该直接去修改ctx.body。

Lambda函数并没有减少koa中间件功能。他们之间完全可以替代和相互转换。

另外,基于这种Hook方式,在项目内可以自行封装一些常用Hook,诸如:useDBuseOrmuseModeluseSessionuseOssuseRedisuseCookieuseValidator等。

高阶和转化

拥抱koa的丰富的中间件生态,并保持简单通用,不必自定义另外的规范,你的项目中应该仅需要Lambda和中间件,并且你可以利用好它们之间的转化。

const { middleware, lambda } = require('koa-lambda-middleware');

// 注:下面例子有些乱,总之属性上挂的是lambda函数

module.exports = {
  // 单个中间件 用中间件来当作lambda
  foo: middleware(async(ctx, next)=>{
    ctx.body = "hello"
    await next()
  }),
  
  // lambda 转 middleware 再转 lambda  套娃😂
  foo2: middleware(lambda(async(a, b)=>{
    return a + b
  })),
   
 // 多个中间件, 数组即可,会合并为一个中间件,内部运作也是一个洋葱模型
  bar: middleware([
    async (ctx, next)=>{
      //todo something
      await next()
    },
    async (ctx, next)=>{
      ctx.body = "hello"
      await next()
    },
    async (ctx, next)=>{
      //todo something
      await next()
    },
  ]),
  
  // 可以套娃 lambda转换为中间件
  baz: middleware([
    async (ctx, next)=>{
      //todo something
      await next()
    },
    lambda(async (a, b)=>{
      return a + b
    }),
    async (ctx, next)=>{
      //todo something
      await next()
    },
    
    //嵌套
    middleware([
      async (ctx, next)=>{
        await next()
      }
      //...
    ])
    
  ])
};

注意:middleware方法的数组内中间件同样遵循 洋葱模型

自定义传参规则

lambda的参数默认约定是ctx.request.body.args数组作为参数,如果要自定义可以对koaLambda.requestParams方法进行重新定义。

const Koa = require('koa');
const koaBody = require('koa-body');
const koaLambda = require('koa-lambda-middleware');
const app = new Koa();


//自定义参数逻辑
koaLambda.requestParams = function(ctx, next){
  //处理获取参数,将参数以数组方式返回
  return [a, b, c...];
};

app.use(koaBody())
    .use(koaLambda({}, app));

app.listen(3333);

其它请求方式

虽然约定了统一使用POST请求,如果项目有这种特殊需求可以对lambda函数的method属性进行设置

module.exports = {
  foo(a, b){
    return a + b
  }
};
//修改foo函数为get方式请求
module.exports.foo.method = 'get';

注意:要使用get请求,还需要对自定义传参requestParams方法内针对get实现获取参数的逻辑。

前端封装示例简易版

这个例子约定使用fetch请求,当然也可以使用axios等请求库去封装实现请求。

const host = `http://localhost:3000`;

// https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
export const fn = async (url:string, ...params:any[]) => {
  let apiUrl = `${host}${url}`;
  try{
    let response = await fetch(apiUrl, {
      mode: 'cors',
      method: 'POST',
      credentials: "include",
      headers: {
        'Content-Type': 'application/json'
      },
      body:JSON.stringify({
        //args是这里默认约定的字段!!
        //它决定了前端传参的方式,
        //如果自定义传参规则,需要修改requestParams方法
        args:[...params]   
      })
    });
    let data = await response.json();
    console.log(`response from:${url}:`, data)
    return data;
  }catch(e){
    console.error(`请求${apiUrl}发生错误`,e)
    throw e; 
  }
};


/**
调用方法:

import {fn} from "./fn";

// 用户列表
export const userList = async function(params = {}){
    return fn('/api/user/list', params);
}

*/

前端 client.js 调用示例

参考文件static/client.js,它是对前面简易版调用封装。 api为生成的对象具备动态响应式路径。

约定:invoke对象调用链路就是接口访问地址路径


import client from './client.js'

// 用client方法返回的invoke对象是一个动态路径的对象
// 动态路径对象说明:
// 简单理解为:invoke对象下的属性有任意多的属性,你可以随意执行诸如:
// invoke.a(); invoke.b()... invoke.z()...
// 甚至 invoke.a 其实也是动态路径对象,所以可以继续链式调用属性,
// 比如:invoke.a.b.c.d.e.f()   任意路径,但这个动态路径是有意义的
// 只需和url路径和后端的路由都保持一致,你就可以不关心接口的url问题

let invoke = client({
    host: 'http://localhost:3333',
    root: '', //根路径 理论上应该与后端koaLambda配置的root一致
});


let foo2 = await invoke.test.foo2(1, 2);
// 调用接口地址/test/foo2
console.log("/test/foo2:", foo2);


let gg = await invoke.test.gg(12, 2);
// 调用接口地址/test/gg
console.log("/test/gg:", gg);


let bar = await invoke.a.b.c.bar();
// 调用接口地址/a/b/c/bar
console.log("/a/b/c/bar:", bar);

特性:

  1. 约定统一默认使用POST请求
  2. 文件目录即对应接口路径
  3. 约定使用args数组作为函数参数,前后端调用一致性
  4. 保持koa洋葱模型,lambda既是函数也是中间件
  5. hook方式获取ctx 和 next,和koa中间件可以互替换
  6. 前端(client.js)搭配,使用动态路径对象访问接口(前端调用链、接口url路径、后端函数路由)三者统一,且无需额外配置