metu

一个TypeScript编写的Node.js WEB框架

Usage no npm install needed!

<script type="module">
  import metu from 'https://cdn.skypack.dev/metu';
</script>

README

一个基于 Node.js 的 web 框架

上手

//index.ts
import Server from 'metu/server'

const bootstrap = async () => {
  const server = new Server()
  await server.create()  //创建应用
  server.run(80)
}

bootstrap()

它的 Server 与 Koa 非常相似(Context不同),使用洋葱模型和 Promise 编写中间件,如果不调用 create 方法创建应用,你可以理解为它就是另一个 Koa

特性

  • TypeScript开发、完善的类型提示以及方法中文注释。
  • 自动加载、支持开发/生产环境热更新。
  • 非侵入式、框架制定了规范但不制定流程。
  • 内置Redis、MySQL、Session等支持,开箱即用。

快速开始

新建您的项目文件夹后安装并创建项目

$ npm install metu
$ npx metu new

运行开发环境,开发环境下会在文件变更时自动重载

$ tsc -w
$ npm run dev

默认监听80端口,浏览器访问:http://127.0.0.1

项目结构

强烈建议您的项目使用 TypeScript 开发。

config/**.json  应用配置、自定义配置
lib             TS编译目录
src             应用根目录
src/app         应用目录
src/index.ts    入口文件
src/plugin.ts   插件配置,应用生命周期
src/type.d.ts   自定义类型,覆盖未知类型
package.json    Node.js项目配置
tsconfig.json   TS编译配置

示例

快速编写一个接口:

//src/app/Home.ts
import { App, Router } from 'metu'

@Router('/home')
export class Demo extends App {
  get() {
    return 'Hello world'
  }
}

浏览器访问:http://127.0.0.1/home 响应:Hello world


自定义匹配路由:

//src/app/Home.ts
import { App, Router } from 'metu'

@Router('/home/:id')
export class Demo extends App {
  get() {
    const params = this.context.url.params()
    return 'Hello ' + params.id[0]
  }
}

浏览器访问:http://127.0.0.1/home/12345 响应:Hello 12345


使用验证器:

//src/app/Home.ts
import { App, Router, Validator, Context } from 'metu'

//定义验证器返回参数[]
function getDemo(context: Context){
  const params = this.context.url.params()
  return [Number(params.id[0])]
}

@Router('/home/:id')
export class Demo extends App {
  @Validator(getDemo)
  get(id: number) {
    return 'Hello ' + id
  }
}

浏览器访问:http://127.0.0.1/home/12345 响应:Hello 12345
通常我们将验证码拆分为不同的模块 import 使用


方法可定义五种名称:get/post/put/delete/all
如果定义all方法,所有请求类型都会被触发all,如果all没有返回值,那么会再次调用对应请求方法

MySQL

//src/app/Home.ts
import { App, Router, Mysql } from 'metu'

@Router('/home')
export class Demo extends App {
  get() {
    const mysql = await Mysql()
    const result = mysql.select('*').from('user').limit(0,1).run()
    mysql.close()
    return result
  }
}

浏览器访问:http://127.0.0.1/home 响应JSON:[{ "name: "value" }]

Redis

//src/app/Home.ts
import { App, Router, Redis } from 'metu'

@Router('/home')
export class Demo extends App {
  get() {
    const scan = await Redis.scan()
    return scan.result
  }
}

浏览器访问:http://127.0.0.1/home 响应JSON:["key1", "key2"]

插件 or 错误处理

定义 src/plugin.ts 来获取应用的周期控制

//src/plugin.ts
import { Plugin, App } from 'metu'

export default class extends Plugin {
  async onError(type: number, error: unknown, app: App | null) {
    this.context.statusCode = 200
    this.context.body = 'error'
  }
  async onResponse(value: unknown, app: App) {
    this.context.statusCode = 200
    this.context.body = value as string
  }
}

type 有五种类型:

  • 0 (构造错误)
  • 1 (验证器错误)
  • 2 (方法错误)
  • 3 (析构方法错误)

他们的流程从上依次执行,遇到错误均会停止后续调用。

进阶

  • 使用基类 定义一个基类基础自 App 管理你应用的用户权限控制,基类很方便的帮助你定义各种API的访问权限
//src/base.ts
import { App, Context } from 'metu'

export class Base extends App {
  declare context: Context & {
    session: {
      userInfo: {
        id: number
        level: number
      }
    }
  }
  constructor(context: Context) {
    super(context)
    if (!context.session.userInfo) {
      throw '请登录账户!' //抛出错误由plugin.ts处理错误输出
    }
  }
}
  • 使用测试器 测试器能在开发环境下 修改文件后/运行后 自动发送一个请求调用目标接口,它可以传递参数
import { App, Context, Router } from 'metu'

@Router('/Tag', {method: 'get'})
export class Tag extends App {
  async get() {
    return {test: 123}
  }
}