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

a2n

v2.1.5

Published

基于express的轻量级服务器

Readme

基于Spring设计理念开发的NodeJs服务端框架(>=V2.1.0)

NPM    Github

✨ 语言

  • 🌍 基于 NodeJs+Express+TS 进行开发
  • 🌈 采用了 Java 语言 Spring 框架的设计理念

✨ 特性

  • 🌍 无需配置即可快速启动项目
  • 🔨 实现了 Spring 中 AOP,IOC,自动配置等主要的功能
  • 🌈 支持自定义装饰器
  • 📦 自动追踪的全局ctx上下文
  • 🔨 根据文件目录和类结构自动生成接口

📦 快速开始

  • 初始化 npm 环境
npm init
  • 安装依赖
npm install a2n
  • package.json 添加启动命令
"scripts": {
  "dev": "a2n dev",
  "build": "a2n build",
  "pnpm:init": "a2n pnpm:init"
}

如果使用的是pnpm,需要在install后运行pnpm pnpm:init

  • 运行服务
npm run dev
  • 打包
npm run build

打包后的产物在根目录dist文件夹内,入口文件为a2n.serve.js,通过node dist/a2n.serve.js运行

🔨 项目结构

  • 配置文件 a2n.config.js

a2n.config.js 为项目配置文件,配置项如下,a2n有一份默认的配置文件,如果需要修改覆盖它,只需要在项目根目录创建名为 a2n.config.js 的文件

module.exports = {
  // 全局接口前缀
  baseUrl: '/api',
  // 导出接口的前缀
  apiExportBaseUrl: '',
  // 组件扫描路径,该路径下的js,ts文件将会被容器扫描,默认src
  componentScan: 'src',
  // 服务启动端口号,默认8080
  port: 8088,
  // 一些自定义的配置项
  datasource: {
    url: 'mysql:127.0.0.1:3306'
  },
  // webpack配置,值可以是以下两种情况
  // 1.一个object,会通过webpack-merge与默认webpack配置进行合并
  // 2.也可以是一个函数,入参: [程序提供的默认webpack配置, webpack-merge的merge函数],返回值:最终的webpack配置
  webpack: {
    ...
  } || (baseWebpackConfig, merge) => ({...})
}
  • ts语言配置

在项目启动时,程序会在根目录默认生成一份ts配置文件,通过修改根目录下的 tsconfig.json 配置ts语言能力。

🔨 配置环境变量

在 2.0.4 版本中,a2n 集成了 dotenv 插件,可以自定义环境变量,在项目启动时,程序会自动加载根目录下的 .env 文件,该文件内容会注入到 process.env 中,如果需要自定义环境变量,可以通过命令行指定 -e 或者 --env 参数指定自定义的环境变量文件。

  • package.json
"scripts": {
  // 不指定 -e 或 --env,默认读取根目录下 .env 文件
  "dev": "a2n dev",
  // 指定任意的参数名称,例如 --env production,读取根目录下 .env.production 文件
  "build": "a2n dev --env production"
},
  • .env
APP_TITLE = a2n服务端框架
  • 在程序中获取环境变量值
console.log(process.env.APP_TITLE)

优先级:通过 -e 或 --env 指定的配置文件如果存在同名参数,则会覆盖默认的 .env 文件

🌈 如何使用

在根目录下创建 src 文件夹(a2n.config.js 中配置的 componentScan 属性,此处以 src 为例),在 src 文件夹下创建 .ts 或 .js 文件,并 export default 导出默认 Class,该 Class 将会被容器扫描注册

编写接口

import { Control, Get, Query, Post, Body, Put, Req, Res, Request, Response } from 'a2n';

/**
 * 使用@Control标记一个Class
 * Class下的@Get,@Post,@Delete,@Put,@RequestMapping方法都会被注册为接口,方法return的值作为接口返回值
 */
@Control("/user")
export default class UserControl{

  @Get("/get")
  get(@Query query: any, @Query("name") name: string){
    // 使用@Query注入url携带的参数,@Query("name")表示注入url参数中名称为name的参数
    return query.name
  }

  @Post("/post")
  get1(@Body body: any, @Body("name") name: string){
    // 使用@Body注入请求报文携带的参数,@Body("name")表示注入请求报文中名称为name的参数
    return body
  }

  @Put("/put")
  get1(@Req request: Request, @Res response: Response){
    // 使用@Req注入请求对象,@Res注入响应对象
    // 请求和响应对象使用参考express的Request、Response使用方法 https://nodejs.cn/express/4x/api/req/
  }

}

依赖注入

import { Autowired, Bean, Config, PostConstruct, BeanScope, Scope, getBean, getBeans } from "a2n";
import RoleService from "./RoleService";
import OtherBean from "./OtherBean";

// @Service将该类交给bean容器管理,与@Bean具有相同的功能,只是命名不同
@Service
// @Scope定义了bean的创建方式,BeanScope.PROTOTYPE:多例,每次获取创建新的bean
// BeanScope.REQUEST,每个请求生成一次bean
// BeanScope.SINGLETON(默认),在单例池生成bean,每次从单例池获取
@Scope(BeanScope.PROTOTYPE)
export default class UserServicer {

  // 获取一个RoleService类型的bean注入到role属性
  // 从2.0.15版本开始,可以省略Autowired的装饰器参数,根据ts类型进行注入
  @Autowired
  role: RoleService

  // 从a2n.config.js配置文件中查询datasource.url注入到url属性中
  @Config('datasource.url')
  url: string = null

  @PostConstruct
  init() {
    // bean创建并完成依赖注入后,将会执行@PostConstruct的内容
    console.log(this.role)
    console.log(this.url)
  }

  async getUser() {
    // 可以通过getBean手动获取bean
    const other = await getBean<OtherBean>(OtherBean)
    // getBeans会获取所有属于或者继承自OtherBean的bean
    const others = await getBeans<OtherBean>(OtherBean)
  }

}

拦截器

import { Bean, Interceptor, Context } from "a2n";

/**
 * 继承Interceptor类并注入到容器中,该类会被注册为拦截器
 * return false拦截请求
 */
@Bean
export default class AuthInterceptor extends Interceptor {

  /**
   * 拦截器校验方法
   * @param req 请求对象
   * @param ctx 请求上下文
   * @returns false:拦截,true:不拦截
   */
  async doFilter(req: Request, ctx: Context): boolean {
    // 从2.1.0版本开始,doFilter的第二个参数变更为ctx,ctx内包含了之前的所有参数内容
    if(ctx.request.baseUrl === "/user") {
      return true
    }else {
      return false
    }
  }

}

自动配置

a2n也提供了SpringBoot中的自动配置功能,通过yarn add或者npm i(暂不支持pnpm)添加到node_modules的依赖包中,如果包含__a2n.inject.js文件,则a2n会自动查找并引入该文件。因此,只要在该文件中引入Bean相关代码,在安装此依赖包时,相关的Bean都会被自动注入容器中

以下是开发一个简单的a2n依赖包的案例

  • 创建一个npm项目(a2n-dep),在项目根目录创建__a2n.inject.js文件,在该文件内引入需要自动注入到容器中的bean image

  • 编写src/User.ts代码,不需要安装a2n直接引入,并使用@Bean装饰器(如果a2n-dep项目中需要引入其他依赖,请添加依赖到peerDependencies中)

import { AppLifecycle, Bean } from 'a2n'

// 继承AppLifecycle的bean都会被注册为生命周期管理器
@Bean
export default class AppLife extends AppLifecycle {
  // 在a2n启动完成后触发该函数
  afterAppStart() {
    console.log('dep start')
  }
  // 在a2n程序关闭时触发函数
  afterAppClose() {
    console.log('app close')
  }
}
  • 效果:当一个a2n项目引入此依赖包(yarn add a2n-dep或npm i a2n-dep)后,a2n项目启动成功后,会打印dep start

自定义装饰器

通过自定义装饰器,配合Aspect切面实现对函数的精准切面控制

  • 定义一个自定义装饰器,装饰需要切入的函数
import { Bean, Context, getContext, registerCustomDecorator } from 'a2n'

export const CustomAspect: MethodDecorator = (target, name) => {
  registerCustomDecorator(CustomAspect, target, name)
}

@Bean
export default class UserService {
  @CustomAspect
  getUser(query: any) {
    const ctx: Context = getContext()
    return ctx.request.path
  }
}
  • 添加一个切面类,在被CustomAspect装饰的函数执行前插入逻辑
import { Aspect, Before } from 'a2n'
import { CustomAspect } from '@/src/UserService'

@Aspect
export default class AspectHandler {
  @Before(CustomAspect)
  before() {
    console.log('before')
  }
}
  • 至此,调用任何装饰了CustomAspect的bean函数,都会触发AspectHandler.before逻辑,例如UserService.getUser函数,

ApiExport自动生成接口

快速将一个类的所有函数导出为接口,主要目的是配合a2n-export-plugin插件使用

// src/api-export.ts
import { ApiExport } from '@core/control/api-export'

@ApiExport
export default class ExportApi {
  async getName(id: number, age: number) {
    return [id, age]
  }
}

在以上demo中,通过为类添加@ApiExport装饰器,生成如下接口

  • url: /api-export/getName
  • body: [id, age]
  • url的生成规则:baseUrl+apiExportBaseUrl+文件路径+函数名(baseUrl和apiExportBaseUrl来源于a2n配置文件,文件路径为基于componentScan的相对路径)
  • body参数的规则:body传参固定为数组,数组中的每个元素会被解构到函数的入参

请求接口

axios.post("/api-export/getName", [id, age])