搭建ts-koa应用

我在实际项目开发过程中,经常要自测,这个时候并不能完全指望服务端 mock 接口和数据,所以其实一段时间了,前前后后沉淀了有这么个 ts-koa 的应用(koa-lite),多用于用于调试接口和 mock 数据,当然,做个改装,也可以用于正式的服务端项目

当然有人要问,为啥不用 nest、egg 这些脚手架,主要原因很简单吧,这些脚手架有额外的上手成本,你要熟悉他们的代码架构,然后相对应的代码文件也比较多,并且功能很齐全。但是咱不需要那么齐全,毕竟初衷是为了用于个人自测项目和 Node.js 学习,一开始就用那么齐全的库,效果反而适得其反

回到项目本身吧,做了一些基础且顺应技术趋势的事情

支持 ts

用 ts-node 编译 ts 代码,nodemon 可以监听文件和热更(根目录下可以通过 nodemon.json 配置规则,参考 nodemon 的配置文件

入口文件

提供了 http 和 websocket 两种应用(app.ts 和 wsApp.ts),分别占用不同的端口,可以配置不同的 script 命令,比如:

"dev": "nodemon -e ts app",
"dev:ws": "nodemon -e ts wsApp",

可以开两个终端同时启动俩应用

websocket 应用是借助了koa-websocket这个中间件实现的,wsApp.ts的基础代码如下:

import Koa from "koa";
import consola from "consola";
import websockify from "koa-websocket";

const wsOptions = {};
const app = websockify(new Koa(), wsOptions);

app.ws.use((ctx, next) => {
  return next(ctx);
});

app.ws.use((ctx) => {
  ctx.websocket.send("===Hello");
  ctx.websocket.on("message", function (msg) {
    console.log("===recive msg", msg);
  });
  ctx.websocket.on("close", () => {
    console.log("===close websocket");
  });
});

/**
 * Create HTTP server.
 */
const host = process.env.HOST || "localhost";
const port = Number(process.env.PORT) || 3001;

const server = app.listen(port, host, () => {
  consola.ready({
    message: `websocket server listening on ws://${host}:${port}`,
    badge: true,
  });
});

http

rest api

借助 koa-router,具体 get、post 接口的实现可以参考项目里的示例

建议不同模块按照前缀区分,然后分为不同文件,在入口文件中引入

//...
app.use(homeRouter.routes()).use(homeRouter.allowedMethods());
app.use(userRouter.routes()).use(router.allowedMethods());

文件接口

借助 koa-body 实现上传。这个中间件不仅用于处理 post 请求的数据,还能处理文件类型,即 multipart/form-data。如果是 koa-bodyparser,还要结合 koa-multer 处理文件数据,而且不能和 koa-body 共用,会有冲突

// app.ts
import koaBody from "koa-body";
//...
app.use(
  koaBody({
    multipart: true,
    formidable: {
      // the max size of uploading file
      maxFileSize: 200 * 1024 * 1024,
    },
  })
);
// homeRouter.ts
function saveFile(file) {
  // 文件读写流
  const reader = fs.createReadStream(file.path);
  let filePath = path.join(__dirname, "/static/upload/") + `/${file.name}`;
  const upStream = fs.createWriteStream(filePath);
  reader.pipe(upStream);
}

homeRouter.post("/upload", async (ctx, next) => {
  // console.log(ctx.request.files);
  const files = ctx.request.files.file;
  // 支持多文件
  if (files.length > 1) {
    for (let file of files) {
      saveFile(file);
    }
  } else if (files) {
    saveFile(files);
  }
  ctx.body = "Upload successfully.";
});

借助 koa-send 实现下载

import send from "koa-send";
// homeRouter.ts
homeRouter.get("/download/:filename", async (ctx, next) => {
  let filename = ctx.params.filename;
  let filePath = `./static/download/${filename}`;
  // mime type, active the download window in browser
  ctx.attachment(filePath);
  await send(ctx, filePath);
  console.log("download successfully.");
});

之前写过一篇 mock 文件接口的文章,参考 mock 文件接口

跨域处理

借助 koa2-cors 定义允许跨域的规则

import cors from "koa2-cors";
//...
app.use(
  cors({
    origin: function (ctx) {
      // 设置允许来自指定域名请求
      if (ctx.url === "/test") {
        return "*";
      }
      return "http://localhost:8080";
    },
    maxAge: 5,
    credentials: true,
    allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allowHeaders: ["Content-Type", "Authorization", "Accept"],
    exposeHeaders: ["WWW-Authenticate", "Server-Authorization"],
  })
);
//...

模板引擎

基于 koa-viewsejs。当然也可以用其他模板引擎,看个人喜好了,我主要是觉得 ejs 更贴近于 html 的形式(对比 pug,缩进那一套确实不太能 get)

import views from "koa-views";
//...
app.use(
  views(path.join(__dirname, "./views"), {
    extension: "ejs",
  })
);

静态资源托管

基于 koa-static,指定一个单独的文件夹来存放静态资源文件

import koaStatic from "koa-static";
//...
// 静态资源目录(相对入口文件app.ts)
const staticPath = "./static";
app.use(koaStatic(path.join(__dirname, staticPath)));

其他

项目相对比较简单,欢迎 comment 和 fork(别忘了 star 哈)。后面主要是个人使用过程中持续迭代,有好的建议欢迎提呀~

仓库地址: https://github.com/GitHubJackson/koa-lite