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 🙏

© 2026 – Pkg Stats / Ryan Hefner

novakite

v2.1.3

Published

A lightweight TypeScript backend framework

Readme

Novakite 使用文档

简介

Novakite 是一个轻量级的 TypeScript 后端框架,内置路由、中间件、参数校验、IP 黑名单、文件上传、Session/Cookie、静态资源、CORS、压缩、超时、优雅关闭等功能,让你用极少的代码构建健壮的 API 服务。


安装

npm install novakite

运行依赖busboy(multipart 解析),框架自动安装。 开发依赖:你需要 tsx 来运行 TypeScript。


项目配置

tsconfig.json(必须开启装饰器)

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "experimentalDecorators": true
  },
  "include": ["src"]
}

package.json(推荐使用 ESM)

{
  "type": "module",
  "scripts": {
    "dev": "tsx src/index.ts"
  }
}

⚠️ 注意:框架本身发布为 ES Module,用户项目最好也采用 ESM("type": "module")以避免兼容问题。


快速开始

目录结构

my-app/
├── src/
│   ├── index.ts
│   └── routes/
│       └── user.controller.ts
├── package.json
└── tsconfig.json

入口文件 src/index.ts

import { Novakite } from 'novakite'

const app = new Novakite()

// 自动扫描 routes 目录,按子目录分组路由
app.scanRoutes('./src/routes')

// 启动服务,默认端口 3000
app.listen()

控制器 src/routes/user.controller.ts

import { Controller, Get, Post, type, HttpException,RouterRequest } from 'novakite'

// 定义校验规则
const CreateUser = type({
  name: 'string',
  email: 'email',
  age: 'number',
})

@Controller()
export class UserController extends RouterRequest {
  @Get('/users')
  list() {
    return { data: [{ id: 1, name: 'John' }] }
  }

  @Get('/users/:id')
  detail() {
    const { id } = this.request.params
    if (id !== '1') throw new HttpException(404, '用户不存在')
    return { data: { id, name: 'John' } }
  }

  @Post('/users', CreateUser)
  create() {
    const body = this.request.body  // 自动校验,类型安全
    return { data: { id: 2, ...body }, message: '创建成功' }
  }
}

启动

npx tsx src/index.ts

核心概念

1. 控制器与路由

使用 @Controller() 装饰器声明控制器,方法上使用 @Get@Post@Put@Delete 装饰路由。

无需传递 request 参数,直接通过 this.request 访问请求上下文(已注入)。

@Controller()
export class MyController extends RouterRequest {
  @Get('/hello')
  hello() {
    return { data: 'world' }
  }
}

路由匹配支持动态参数:

@Get('/posts/:year/:month')
getPost() {
  const { year, month } = this.request.params
}

路由分组:目录结构自动映射为路由前缀。

src/routes/
├── admin/           → 前缀 /admin
│   └── dashboard.controller.ts
├── user.controller.ts → 无前缀

scanRoutes('./src/routes') 会自动处理。

2. 请求上下文 this.request

通过 this.request 可以获取所有请求相关信息:

| 属性/方法 | 说明 | | :----------------------------------------------------------- | :-------------------------------------------- | | this.request.params | 路径参数 | | this.request.query | 查询参数 | | this.request.body | 请求体(JSON/form/multipart 自动解析) | | this.request.ip | 客户端 IP(透明代理下可取 X-Forwarded-For) | | this.request.method | HTTP 方法 | | this.request.path | 请求路径 | | this.request.headers | 请求头 | | this.request.global.token | 从 Authorization 头解析的 token(可配置前缀) | | this.request.global.cookie.get(name) | 读取 Cookie(只读) | | this.request.global.set(key, value) / get(key) | 请求级临时存储 | | this.request.cookies.get(name) / set(name,value,opts) / delete(name) | Cookie 操作 | | this.request.session.get(key) / set(key,value) / destroy() | Session 操作 | | this.request.file(fieldname) | 单个上传文件 | | this.request.files(fieldname) | 多个上传文件 | | this.request.allFiles() | 所有上传文件 | | this.request.setHeader(key, value) | 设置响应头 | | this.request.status(code) | 设置 HTTP 状态码 |

3. 响应

普通 JSON 响应:直接 return 一个对象,框架自动包裹成统一格式 { code, data, message }

return { data: user };  // { code:200, data:user, message:"success" }
return { data: user, code:1000, message:'自定义' };

设置 HTTP 状态码和响应头

return { data: {}, __code__: 201 };                   // HTTP 201
return { data: {}, __handler__: { 'X-Custom': 'val' } }; // 自定义头

设置 Cookie(响应阶段)

return {
  data: {},
  __cookie__: [{ name: 'token', value: 'xxx', maxAge: 3600, httpOnly: true }]
};

特殊响应:使用 ResFilesresRedirectUrlSSR 工具函数。

import { ResFiles, resRedirectUrl, SSR } from 'novakite'

// 文件下载
return ResFiles.returnFile('./report.xlsx', '报表.xlsx');

// 流式响应(SSE、视频流等)
const stream = createReadStream('./video.mp4');
return ResFiles.stream(stream, 'video/mp4');

// 空响应 204
return ResFiles.empty();

// 重定向
return resRedirectUrl('/new', 301);

// SSR 渲染(需先注入渲染器)
return SSR.renderPage(App, { user: {...} });
// 或直接返回 HTML 字符串
return SSR.html('<h1>Hello</h1>');

配置项

创建应用时可传入配置:

const app = new Novakite({
  // 存储驱动(默认本地 ./uploads)
  storage: new LocalStorageDriver('./uploads'),

  // 静态资源
  static: [
    { name: 'public', path: './public' },
    { name: 'assets', path: './dist/assets' },
  ],

  // CORS(传入 cors 包实例)
  cors: cors({ origin: '*' }),

  // Session 配置
  session: {
    secret: 'my-secret',
    maxAge: 86400,  // 秒,默认 24h
    store: new RedisStore(...)  // 可选,不传则使用内存存储
  },

  // 请求超时(毫秒),默认无限制
  timeout: 30000,

  // gzip 压缩,默认开启,阈值 1024 字节
  compress: { threshold: 2048 },  // 设为 false 关闭

  // token 解析前缀,默认 false 不解析
  auth: 'Bearer',

  // SSR 渲染器注入
  ssr: {
    render: (component, props) => renderToString(component)
  }
})

listen 方法

app.listen();                         // 3000, debug false
app.listen(3000);                    // 3000
app.listen({ port: 3000, debug: true });
app.listen({ debug: true });
app.listen(config => {
  config.port = 8080;
  config.debug = true;
});
app.listen({ port: 3000 }, () => console.log('ready'));

debug: true 会开启 /__routes 路由信息查看。


中间件

全局中间件:使用 app.use(mw)app.useArrFuns([mw1, mw2])

app.use(async (request, next) => {
  console.log('before')
  await next()
  console.log('after')
})

分组中间件:使用 @GroupMiddleware 装饰器,应用于整个控制器。

@GroupMiddleware(authMiddleware)
@Controller()
export class AdminController { ... }

路由中间件:使用 @Use(mw) 装饰器,应用于单个路由。

@Get('/profile')
@Use(authMiddleware)
profile() { ... }

中间件执行顺序:全局 → 分组 → 路由级(@Blacklist@Use)→ 处理器。


参数校验

使用 type() 定义校验模板,传给 @Post 的第二个参数。

const LoginSchema = type({ username: 'string', password: 'string' });

@Post('/login', LoginSchema)
login() {
  const { username, password } = this.request.body;
}

支持的字段类型:'string' | 'number' | 'boolean' | 'email' | 'url' | 'date'


IP 黑名单

在项目根目录放置 BU.json

{
  "blacklist": ["192.168.1.1", "10.0.0.5"]
}

在路由上使用 @Blacklist() 装饰器,也可以追加额外 IP:

@Get('/admin')
@Blacklist()
admin() { ... }

@Get('/sensitive')
@Blacklist({ ips: ['192.168.1.100'] })
sensitive() { ... }

命中返回 { code: 403, data: {}, message: "IP blocked" }


文件上传

支持 multipart/form-data,文件通过 this.request.file() 访问。

@Post('/upload')
async upload() {
  const file = this.request.file('avatar');  // 取第一个文件
  const files = this.request.files('photos'); // 取全部同名文件

  // 保存文件(使用全局存储驱动)
  const url = await file.save();  // 或 file.saveWith(driver, name)
  return { data: { url } };
}

上传进度

this.request.onProgress(percent => console.log(`${percent}%`));

文件大小限制(路由级别):

@Post('/upload', undefined, { maxFileSize: 10 * 1024 * 1024 })

Session

框架自动通过 novakite_session Cookie 维护 Session。

this.request.session.set('userId', '123');
const uid = this.request.session.get('userId');
this.request.session.destroy();

Session 数据在响应结束时自动保存。默认使用内存存储,可自定义存储(实现 SessionStore 接口)。


Cookie

读取:this.request.cookies.get('token')this.request.global.cookie.get('token')。 设置:

  • 在 Handler 中通过 return { __cookie__: [...] } 批量设置(推荐)
  • 在中间件中通过 this.request.cookies.set(name, value, options) 设置

WebSocket

框架暴露 app.server(getter),可接入任何 WebSocket 库。

import { WebSocketServer } from 'ws'

const wss = new WebSocketServer({ server: app.server })
wss.on('connection', ws => { ... })

优雅关闭

process.on('SIGTERM', () => app.close());       // 优雅关闭,等待现有请求完成(默认 10s 超时)
process.on('SIGINT', () => app.forceClose());   // 立即强制关闭

自定义超时:app.close({ timeout: 5000 })


路由信息查看

debug: true 时,访问 GET /__routes 查看所有路由,按分组展示。

[
  {
    "name": "users",
    "prefix": "/users",
    "routes": [
      { "method": "GET", "path": "/", "fullPath": "/users", "handler": "list", ... }
    ]
  }
]

SSR 支持

注入渲染器

const app = new Novakite({
  ssr: {
    render: (component, props) => renderToString(component)
  }
})

控制器中渲染

import { SSR } from 'novakite'

@Get('/')
home() {
  return SSR.renderPage(App, { title: 'Home' });
}

注意事项

  1. 运行环境:必须使用 tsx 运行,不支持 ts-node
  2. 装饰器tsconfig.json 必须设置 "experimentalDecorators": true
  3. 模块系统:框架为 ES Module,用户项目推荐使用 "type": "module",避免混合 CJS 导致问题。
  4. 控制器:类必须使用 @Controller() 装饰,且类名大写开头,否则不会被自动扫描。
  5. 请求对象不要手动在方法参数中接收 request,直接通过 this.request 访问。
  6. 返回数据:普通 JSON 直接 return 对象;特殊响应使用 ResFilesresRedirectUrlSSR
  7. 参数校验:仅在 @Post 装饰器中传入 type()@Get 等不需要。
  8. IP 黑名单:需要项目根目录存在 BU.json,文件不存在不会生效。
  9. Session 持久化:默认内存存储,生产环境建议传入自定义 store(如 Redis)。
  10. 文件上传:依赖 busboy,框架已内置,无需额外安装。
  11. 静态资源:配置 static 字段,支持多目录。
  12. CORS:需要自己安装 cors 包并传入 cors() 实例。
  13. 压缩:gzip 默认开启,阈值 1KB,可关闭或调整。
  14. 优雅关闭app.close() 会等待现有请求处理完,建议监听 SIGTERM
  15. WebSocket:通过 app.server 获取原生 Server 对象。

特别强调

🔔 务必使用 @Controller() 并且不传参数给路由方法 Controller 类需要 @Controller() 装饰,路由方法直接通过 this.request 获取请求信息,不要再写 (request: Context) 参数

🔔 响应统一通过 return 完成 所有响应(JSON、文件、流、重定向、HTML)都通过 return 语句返回,框架自动处理。

🔔 SSR 需要提前注入渲染器 要使用 SSR.renderPage(),必须在 Novakite 构造函数中配置 ssr.render,否则会报错。

🔔 this.request.body 的类型 目前 bodyRecord<string, unknown> | null,需要手动断言为具体类型。未来会增强类型推导。

🔔 __routes 默认关闭 只有 listen({ debug: true }) 时才能访问,生产环境请勿开启。

🔔 导入类型时使用 import typenovakite 导入仅用于类型的接口时,请使用 import type { ... } from 'novakite' 以减小编译开销。